diff --git a/go.mod b/go.mod index 1b5ed64..02c4d73 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,11 @@ go 1.13 require ( github.com/creack/pty v1.1.9 github.com/pmezard/go-difflib v1.0.0 - github.com/visualfc/gomod v0.1.0 + github.com/visualfc/gomod v0.1.1 github.com/visualfc/goversion v1.1.0 - golang.org/x/tools v0.1.10 + golang.org/x/mod v0.7.0 + golang.org/x/sys v0.2.0 + golang.org/x/text v0.4.0 + golang.org/x/tools v0.3.0 + golang.org/x/vuln v0.0.0-20221122171214-05fb7250142c ) diff --git a/go.sum b/go.sum index 9efd22d..d6d166d 100644 --- a/go.sum +++ b/go.sum @@ -1,38 +1,105 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/visualfc/gomod v0.1.0 h1:qSGKJnTu9FjErza0e+ERm+uZYmqPSzjNq2pfEUIx0rQ= -github.com/visualfc/gomod v0.1.0/go.mod h1:rV5goiA/Ul6yT8X2eDnc/dl0dVy0cDHJLZVOuJ8PdmM= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/visualfc/gomod v0.1.1 h1:17IpmvYEWjH+owa+J9gZPG0lFXL9f5wIBDGF0q3ut8g= +github.com/visualfc/gomod v0.1.1/go.mod h1:rV5goiA/Ul6yT8X2eDnc/dl0dVy0cDHJLZVOuJ8PdmM= github.com/visualfc/goversion v1.1.0 h1:EN0YQGRkeGoWTPxPNTnbhyNQyas5leKH5U5lL4t8lRE= github.com/visualfc/goversion v1.1.0/go.mod h1:Gr3s6bW8NTomhheImwAttqno97Mw6pAnFn2dU8/EMa8= -github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.1-0.20221108172846-9474ca31d0df/go.mod h1:3zSr343Sn+jgvZ3zUJzhHuM8sdsvadvIdcTE45JuvsA= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/vuln v0.0.0-20221122171214-05fb7250142c h1:Q/cUnXhEEKm8vd19JItKXGfjQl2Tts0p7mR0uXW7LJE= +golang.org/x/vuln v0.0.0-20221122171214-05fb7250142c/go.mod h1:8nFLBv8KFyZ2VuczUYssYKh+fcBR3BuXDG/HIWcxlwM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk= +honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio= +mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5/go.mod h1:b8RRCBm0eeiWR8cfN88xeq2G5SG3VKGO+5UPWi5FSOY= diff --git a/gopls/client.go b/gopls/client.go new file mode 100644 index 0000000..a272fef --- /dev/null +++ b/gopls/client.go @@ -0,0 +1,58 @@ +package gopls + +import ( + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +type Client struct { +} + +var _ protocol.Client = &Client{} + +func (c *Client) LogTrace(context.Context, *protocol.LogTraceParams) error { + return nil +} +func (c *Client) Progress(context.Context, *protocol.ProgressParams) error { + return nil +} +func (c *Client) RegisterCapability(context.Context, *protocol.RegistrationParams) error { + return nil +} +func (c *Client) UnregisterCapability(context.Context, *protocol.UnregistrationParams) error { + return nil +} +func (c *Client) Event(context.Context, *interface{}) error { + return nil +} +func (c *Client) PublishDiagnostics(context.Context, *protocol.PublishDiagnosticsParams) error { + return nil +} +func (c *Client) LogMessage(context.Context, *protocol.LogMessageParams) error { + return nil +} +func (c *Client) ShowDocument(context.Context, *protocol.ShowDocumentParams) (*protocol.ShowDocumentResult, error) { + return nil, nil +} +func (c *Client) ShowMessage(context.Context, *protocol.ShowMessageParams) error { + return nil +} +func (c *Client) ShowMessageRequest(context.Context, *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) { + return nil, nil +} +func (c *Client) WorkDoneProgressCreate(context.Context, *protocol.WorkDoneProgressCreateParams) error { + return nil +} +func (c *Client) ApplyEdit(context.Context, *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResult, error) { + return nil, nil +} +func (c *Client) CodeLensRefresh(context.Context) error { + return nil +} +func (c *Client) Configuration(context.Context, *protocol.ParamConfiguration) ([]protocol.LSPAny, error) { + return nil, nil +} +func (c *Client) WorkspaceFolders(context.Context) ([]protocol.WorkspaceFolder, error) { + return nil, nil +} diff --git a/gopls/golang_org_x_tools/LICENSE b/gopls/golang_org_x_tools/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/gopls/golang_org_x_tools/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gopls/golang_org_x_tools/analysisinternal/analysis.go b/gopls/golang_org_x_tools/analysisinternal/analysis.go new file mode 100644 index 0000000..6fceef5 --- /dev/null +++ b/gopls/golang_org_x_tools/analysisinternal/analysis.go @@ -0,0 +1,402 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package analysisinternal exposes internal-only fields from go/analysis. +package analysisinternal + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" + "go/types" + "strconv" +) + +// DiagnoseFuzzTests controls whether the 'tests' analyzer diagnoses fuzz tests +// in Go 1.18+. +var DiagnoseFuzzTests bool = false + +var ( + GetTypeErrors func(p interface{}) []types.Error + SetTypeErrors func(p interface{}, errors []types.Error) +) + +func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos { + // Get the end position for the type error. + offset, end := fset.PositionFor(start, false).Offset, start + if offset >= len(src) { + return end + } + if width := bytes.IndexAny(src[offset:], " \n,():;[]+-*"); width > 0 { + end = start + token.Pos(width) + } + return end +} + +func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + under := typ + if n, ok := typ.(*types.Named); ok { + under = n.Underlying() + } + switch u := under.(type) { + case *types.Basic: + switch { + case u.Info()&types.IsNumeric != 0: + return &ast.BasicLit{Kind: token.INT, Value: "0"} + case u.Info()&types.IsBoolean != 0: + return &ast.Ident{Name: "false"} + case u.Info()&types.IsString != 0: + return &ast.BasicLit{Kind: token.STRING, Value: `""`} + default: + panic("unknown basic type") + } + case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array: + return ast.NewIdent("nil") + case *types.Struct: + texpr := TypeExpr(f, pkg, typ) // typ because we want the name here. + if texpr == nil { + return nil + } + return &ast.CompositeLit{ + Type: texpr, + } + } + return nil +} + +// IsZeroValue checks whether the given expression is a 'zero value' (as determined by output of +// analysisinternal.ZeroValue) +func IsZeroValue(expr ast.Expr) bool { + switch e := expr.(type) { + case *ast.BasicLit: + return e.Value == "0" || e.Value == `""` + case *ast.Ident: + return e.Name == "nil" || e.Name == "false" + default: + return false + } +} + +// TypeExpr returns syntax for the specified type. References to +// named types from packages other than pkg are qualified by an appropriate +// package name, as defined by the import environment of file. +func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + switch t := typ.(type) { + case *types.Basic: + switch t.Kind() { + case types.UnsafePointer: + return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")} + default: + return ast.NewIdent(t.Name()) + } + case *types.Pointer: + x := TypeExpr(f, pkg, t.Elem()) + if x == nil { + return nil + } + return &ast.UnaryExpr{ + Op: token.MUL, + X: x, + } + case *types.Array: + elt := TypeExpr(f, pkg, t.Elem()) + if elt == nil { + return nil + } + return &ast.ArrayType{ + Len: &ast.BasicLit{ + Kind: token.INT, + Value: fmt.Sprintf("%d", t.Len()), + }, + Elt: elt, + } + case *types.Slice: + elt := TypeExpr(f, pkg, t.Elem()) + if elt == nil { + return nil + } + return &ast.ArrayType{ + Elt: elt, + } + case *types.Map: + key := TypeExpr(f, pkg, t.Key()) + value := TypeExpr(f, pkg, t.Elem()) + if key == nil || value == nil { + return nil + } + return &ast.MapType{ + Key: key, + Value: value, + } + case *types.Chan: + dir := ast.ChanDir(t.Dir()) + if t.Dir() == types.SendRecv { + dir = ast.SEND | ast.RECV + } + value := TypeExpr(f, pkg, t.Elem()) + if value == nil { + return nil + } + return &ast.ChanType{ + Dir: dir, + Value: value, + } + case *types.Signature: + var params []*ast.Field + for i := 0; i < t.Params().Len(); i++ { + p := TypeExpr(f, pkg, t.Params().At(i).Type()) + if p == nil { + return nil + } + params = append(params, &ast.Field{ + Type: p, + Names: []*ast.Ident{ + { + Name: t.Params().At(i).Name(), + }, + }, + }) + } + var returns []*ast.Field + for i := 0; i < t.Results().Len(); i++ { + r := TypeExpr(f, pkg, t.Results().At(i).Type()) + if r == nil { + return nil + } + returns = append(returns, &ast.Field{ + Type: r, + }) + } + return &ast.FuncType{ + Params: &ast.FieldList{ + List: params, + }, + Results: &ast.FieldList{ + List: returns, + }, + } + case *types.Named: + if t.Obj().Pkg() == nil { + return ast.NewIdent(t.Obj().Name()) + } + if t.Obj().Pkg() == pkg { + return ast.NewIdent(t.Obj().Name()) + } + pkgName := t.Obj().Pkg().Name() + + // If the file already imports the package under another name, use that. + for _, cand := range f.Imports { + if path, _ := strconv.Unquote(cand.Path.Value); path == t.Obj().Pkg().Path() { + if cand.Name != nil && cand.Name.Name != "" { + pkgName = cand.Name.Name + } + } + } + if pkgName == "." { + return ast.NewIdent(t.Obj().Name()) + } + return &ast.SelectorExpr{ + X: ast.NewIdent(pkgName), + Sel: ast.NewIdent(t.Obj().Name()), + } + case *types.Struct: + return ast.NewIdent(t.String()) + case *types.Interface: + return ast.NewIdent(t.String()) + default: + return nil + } +} + +type TypeErrorPass string + +const ( + NoNewVars TypeErrorPass = "nonewvars" + NoResultValues TypeErrorPass = "noresultvalues" + UndeclaredName TypeErrorPass = "undeclaredname" +) + +// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable. +// Some examples: +// +// Basic Example: +// z := 1 +// y := z + x +// If x is undeclared, then this function would return `y := z + x`, so that we +// can insert `x := ` on the line before `y := z + x`. +// +// If stmt example: +// if z == 1 { +// } else if z == y {} +// If y is undeclared, then this function would return `if z == 1 {`, because we cannot +// insert a statement between an if and an else if statement. As a result, we need to find +// the top of the if chain to insert `y := ` before. +func StmtToInsertVarBefore(path []ast.Node) ast.Stmt { + enclosingIndex := -1 + for i, p := range path { + if _, ok := p.(ast.Stmt); ok { + enclosingIndex = i + break + } + } + if enclosingIndex == -1 { + return nil + } + enclosingStmt := path[enclosingIndex] + switch enclosingStmt.(type) { + case *ast.IfStmt: + // The enclosingStmt is inside of the if declaration, + // We need to check if we are in an else-if stmt and + // get the base if statement. + return baseIfStmt(path, enclosingIndex) + case *ast.CaseClause: + // Get the enclosing switch stmt if the enclosingStmt is + // inside of the case statement. + for i := enclosingIndex + 1; i < len(path); i++ { + if node, ok := path[i].(*ast.SwitchStmt); ok { + return node + } else if node, ok := path[i].(*ast.TypeSwitchStmt); ok { + return node + } + } + } + if len(path) <= enclosingIndex+1 { + return enclosingStmt.(ast.Stmt) + } + // Check if the enclosing statement is inside another node. + switch expr := path[enclosingIndex+1].(type) { + case *ast.IfStmt: + // Get the base if statement. + return baseIfStmt(path, enclosingIndex+1) + case *ast.ForStmt: + if expr.Init == enclosingStmt || expr.Post == enclosingStmt { + return expr + } + } + return enclosingStmt.(ast.Stmt) +} + +// baseIfStmt walks up the if/else-if chain until we get to +// the top of the current if chain. +func baseIfStmt(path []ast.Node, index int) ast.Stmt { + stmt := path[index] + for i := index + 1; i < len(path); i++ { + if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt { + stmt = node + continue + } + break + } + return stmt.(ast.Stmt) +} + +// WalkASTWithParent walks the AST rooted at n. The semantics are +// similar to ast.Inspect except it does not call f(nil). +func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) { + var ancestors []ast.Node + ast.Inspect(n, func(n ast.Node) (recurse bool) { + if n == nil { + ancestors = ancestors[:len(ancestors)-1] + return false + } + + var parent ast.Node + if len(ancestors) > 0 { + parent = ancestors[len(ancestors)-1] + } + ancestors = append(ancestors, n) + return f(n, parent) + }) +} + +// MatchingIdents finds the names of all identifiers in 'node' that match any of the given types. +// 'pos' represents the position at which the identifiers may be inserted. 'pos' must be within +// the scope of each of identifier we select. Otherwise, we will insert a variable at 'pos' that +// is unrecognized. +func MatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]string { + + // Initialize matches to contain the variable types we are searching for. + matches := make(map[types.Type][]string) + for _, typ := range typs { + if typ == nil { + continue // TODO(adonovan): is this reachable? + } + matches[typ] = nil // create entry + } + + seen := map[types.Object]struct{}{} + ast.Inspect(node, func(n ast.Node) bool { + if n == nil { + return false + } + // Prevent circular definitions. If 'pos' is within an assignment statement, do not + // allow any identifiers in that assignment statement to be selected. Otherwise, + // we could do the following, where 'x' satisfies the type of 'f0': + // + // x := fakeStruct{f0: x} + // + if assign, ok := n.(*ast.AssignStmt); ok && pos > assign.Pos() && pos <= assign.End() { + return false + } + if n.End() > pos { + return n.Pos() <= pos + } + ident, ok := n.(*ast.Ident) + if !ok || ident.Name == "_" { + return true + } + obj := info.Defs[ident] + if obj == nil || obj.Type() == nil { + return true + } + if _, ok := obj.(*types.TypeName); ok { + return true + } + // Prevent duplicates in matches' values. + if _, ok = seen[obj]; ok { + return true + } + seen[obj] = struct{}{} + // Find the scope for the given position. Then, check whether the object + // exists within the scope. + innerScope := pkg.Scope().Innermost(pos) + if innerScope == nil { + return true + } + _, foundObj := innerScope.LookupParent(ident.Name, pos) + if foundObj != obj { + return true + } + // The object must match one of the types that we are searching for. + // TODO(adonovan): opt: use typeutil.Map? + if names, ok := matches[obj.Type()]; ok { + matches[obj.Type()] = append(names, ident.Name) + } else { + // If the object type does not exactly match + // any of the target types, greedily find the first + // target type that the object type can satisfy. + for typ := range matches { + if equivalentTypes(obj.Type(), typ) { + matches[typ] = append(matches[typ], ident.Name) + } + } + } + return true + }) + return matches +} + +func equivalentTypes(want, got types.Type) bool { + if types.Identical(want, got) { + return true + } + // Code segment to help check for untyped equality from (golang/go#32146). + if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 { + if lhs, ok := got.Underlying().(*types.Basic); ok { + return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType + } + } + return types.AssignableTo(want, got) +} diff --git a/gopls/golang_org_x_tools/bug/bug.go b/gopls/golang_org_x_tools/bug/bug.go new file mode 100644 index 0000000..b974e88 --- /dev/null +++ b/gopls/golang_org_x_tools/bug/bug.go @@ -0,0 +1,128 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bug provides utilities for reporting internal bugs, and being +// notified when they occur. +// +// Philosophically, because gopls runs as a sidecar process that the user does +// not directly control, sometimes it keeps going on broken invariants rather +// than panicking. In those cases, bug reports provide a mechanism to alert +// developers and capture relevant metadata. +package bug + +import ( + "fmt" + "runtime" + "runtime/debug" + "sort" + "sync" +) + +// PanicOnBugs controls whether to panic when bugs are reported. +// +// It may be set to true during testing. +var PanicOnBugs = false + +var ( + mu sync.Mutex + exemplars map[string]Bug + waiters []chan<- Bug +) + +// A Bug represents an unexpected event or broken invariant. They are used for +// capturing metadata that helps us understand the event. +type Bug struct { + File string // file containing the call to bug.Report + Line int // line containing the call to bug.Report + Description string // description of the bug + Data Data // additional metadata + Key string // key identifying the bug (file:line if available) + Stack string // call stack +} + +// Data is additional metadata to record for a bug. +type Data map[string]interface{} + +// Reportf reports a formatted bug message. +func Reportf(format string, args ...interface{}) { + Report(fmt.Sprintf(format, args...), nil) +} + +// Errorf calls fmt.Errorf for the given arguments, and reports the resulting +// error message as a bug. +func Errorf(format string, args ...interface{}) error { + err := fmt.Errorf(format, args...) + Report(err.Error(), nil) + return err +} + +// Report records a new bug encountered on the server. +// It uses reflection to report the position of the immediate caller. +func Report(description string, data Data) { + _, file, line, ok := runtime.Caller(1) + + key := "" + if ok { + key = fmt.Sprintf("%s:%d", file, line) + } + + if PanicOnBugs { + panic(fmt.Sprintf("%s: %s", key, description)) + } + + bug := Bug{ + File: file, + Line: line, + Description: description, + Data: data, + Key: key, + Stack: string(debug.Stack()), + } + + mu.Lock() + defer mu.Unlock() + + if exemplars == nil { + exemplars = make(map[string]Bug) + } + + if _, ok := exemplars[key]; !ok { + exemplars[key] = bug // capture one exemplar per key + } + + for _, waiter := range waiters { + waiter <- bug + } + waiters = nil +} + +// Notify returns a channel that will be sent the next bug to occur on the +// server. This channel only ever receives one bug. +func Notify() <-chan Bug { + mu.Lock() + defer mu.Unlock() + + ch := make(chan Bug, 1) // 1-buffered so that bug reporting is non-blocking + waiters = append(waiters, ch) + return ch +} + +// List returns a slice of bug exemplars -- the first bugs to occur at each +// callsite. +func List() []Bug { + mu.Lock() + defer mu.Unlock() + + var bugs []Bug + + for _, bug := range exemplars { + bugs = append(bugs, bug) + } + + sort.Slice(bugs, func(i, j int) bool { + return bugs[i].Key < bugs[j].Key + }) + + return bugs +} diff --git a/gopls/golang_org_x_tools/diff/diff.go b/gopls/golang_org_x_tools/diff/diff.go new file mode 100644 index 0000000..7b08ad5 --- /dev/null +++ b/gopls/golang_org_x_tools/diff/diff.go @@ -0,0 +1,161 @@ +// 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 diff computes differences between text files or strings. +package diff + +import ( + "fmt" + "sort" + "strings" +) + +// An Edit describes the replacement of a portion of a text file. +type Edit struct { + Start, End int // byte offsets of the region to replace + New string // the replacement +} + +// Apply applies a sequence of edits to the src buffer and returns the +// result. Edits are applied in order of start offset; edits with the +// same start offset are applied in they order they were provided. +// +// Apply returns an error if any edit is out of bounds, +// or if any pair of edits is overlapping. +func Apply(src string, edits []Edit) (string, error) { + edits, size, err := validate(src, edits) + if err != nil { + return "", err + } + + // Apply edits. + out := make([]byte, 0, size) + lastEnd := 0 + for _, edit := range edits { + if lastEnd < edit.Start { + out = append(out, src[lastEnd:edit.Start]...) + } + out = append(out, edit.New...) + lastEnd = edit.End + } + out = append(out, src[lastEnd:]...) + + if len(out) != size { + panic("wrong size") + } + + return string(out), nil +} + +// validate checks that edits are consistent with src, +// and returns the size of the patched output. +// It may return a different slice. +func validate(src string, edits []Edit) ([]Edit, int, error) { + if !sort.IsSorted(editsSort(edits)) { + edits = append([]Edit(nil), edits...) + SortEdits(edits) + } + + // Check validity of edits and compute final size. + size := len(src) + lastEnd := 0 + for _, edit := range edits { + if !(0 <= edit.Start && edit.Start <= edit.End && edit.End <= len(src)) { + return nil, 0, fmt.Errorf("diff has out-of-bounds edits") + } + if edit.Start < lastEnd { + return nil, 0, fmt.Errorf("diff has overlapping edits") + } + size += len(edit.New) + edit.Start - edit.End + lastEnd = edit.End + } + + return edits, size, nil +} + +// SortEdits orders a slice of Edits by (start, end) offset. +// This ordering puts insertions (end = start) before deletions +// (end > start) at the same point, but uses a stable sort to preserve +// the order of multiple insertions at the same point. +// (Apply detects multiple deletions at the same point as an error.) +func SortEdits(edits []Edit) { + sort.Stable(editsSort(edits)) +} + +type editsSort []Edit + +func (a editsSort) Len() int { return len(a) } +func (a editsSort) Less(i, j int) bool { + if cmp := a[i].Start - a[j].Start; cmp != 0 { + return cmp < 0 + } + return a[i].End < a[j].End +} +func (a editsSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// lineEdits expands and merges a sequence of edits so that each +// resulting edit replaces one or more complete lines. +// See ApplyEdits for preconditions. +func lineEdits(src string, edits []Edit) ([]Edit, error) { + edits, _, err := validate(src, edits) + if err != nil { + return nil, err + } + + // Do all edits begin and end at the start of a line? + // TODO(adonovan): opt: is this fast path necessary? + // (Also, it complicates the result ownership.) + for _, edit := range edits { + if edit.Start >= len(src) || // insertion at EOF + edit.Start > 0 && src[edit.Start-1] != '\n' || // not at line start + edit.End > 0 && src[edit.End-1] != '\n' { // not at line start + goto expand + } + } + return edits, nil // aligned + +expand: + expanded := make([]Edit, 0, len(edits)) // a guess + prev := edits[0] + // TODO(adonovan): opt: start from the first misaligned edit. + // TODO(adonovan): opt: avoid quadratic cost of string += string. + for _, edit := range edits[1:] { + between := src[prev.End:edit.Start] + if !strings.Contains(between, "\n") { + // overlapping lines: combine with previous edit. + prev.New += between + edit.New + prev.End = edit.End + } else { + // non-overlapping lines: flush previous edit. + expanded = append(expanded, expandEdit(prev, src)) + prev = edit + } + } + return append(expanded, expandEdit(prev, src)), nil // flush final edit +} + +// expandEdit returns edit expanded to complete whole lines. +func expandEdit(edit Edit, src string) Edit { + // Expand start left to start of line. + // (delta is the zero-based column number of of start.) + start := edit.Start + if delta := start - 1 - strings.LastIndex(src[:start], "\n"); delta > 0 { + edit.Start -= delta + edit.New = src[start-delta:start] + edit.New + } + + // Expand end right to end of line. + // (endCol is the zero-based column number of end.) + end := edit.End + if endCol := end - 1 - strings.LastIndex(src[:end], "\n"); endCol > 0 { + if nl := strings.IndexByte(src[end:], '\n'); nl < 0 { + edit.End = len(src) // extend to EOF + } else { + edit.End = end + nl + 1 // extend beyond \n + } + edit.New += src[end:edit.End] + } + + return edit +} diff --git a/gopls/golang_org_x_tools/diff/lcs/common.go b/gopls/golang_org_x_tools/diff/lcs/common.go new file mode 100644 index 0000000..e5d0801 --- /dev/null +++ b/gopls/golang_org_x_tools/diff/lcs/common.go @@ -0,0 +1,184 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lcs + +import ( + "log" + "sort" +) + +// lcs is a longest common sequence +type lcs []diag + +// A diag is a piece of the edit graph where A[X+i] == B[Y+i], for 0<=i l[j].Len + }) + return l +} + +// validate that the elements of the lcs do not overlap +// (can only happen when the two-sided algorithm ends early) +// expects the lcs to be sorted +func (l lcs) valid() bool { + for i := 1; i < len(l); i++ { + if l[i-1].X+l[i-1].Len > l[i].X { + return false + } + if l[i-1].Y+l[i-1].Len > l[i].Y { + return false + } + } + return true +} + +// repair overlapping lcs +// only called if two-sided stops early +func (l lcs) fix() lcs { + // from the set of diagonals in l, find a maximal non-conflicting set + // this problem may be NP-complete, but we use a greedy heuristic, + // which is quadratic, but with a better data structure, could be D log D. + // indepedent is not enough: {0,3,1} and {3,0,2} can't both occur in an lcs + // which has to have monotone x and y + if len(l) == 0 { + return nil + } + sort.Slice(l, func(i, j int) bool { return l[i].Len > l[j].Len }) + tmp := make(lcs, 0, len(l)) + tmp = append(tmp, l[0]) + for i := 1; i < len(l); i++ { + var dir direction + nxt := l[i] + for _, in := range tmp { + if dir, nxt = overlap(in, nxt); dir == empty || dir == bad { + break + } + } + if nxt.Len > 0 && dir != bad { + tmp = append(tmp, nxt) + } + } + tmp.sort() + if false && !tmp.valid() { // debug checking + log.Fatalf("here %d", len(tmp)) + } + return tmp +} + +type direction int + +const ( + empty direction = iota // diag is empty (so not in lcs) + leftdown // proposed acceptably to the left and below + rightup // proposed diag is acceptably to the right and above + bad // proposed diag is inconsistent with the lcs so far +) + +// overlap trims the proposed diag prop so it doesn't overlap with +// the existing diag that has already been added to the lcs. +func overlap(exist, prop diag) (direction, diag) { + if prop.X <= exist.X && exist.X < prop.X+prop.Len { + // remove the end of prop where it overlaps with the X end of exist + delta := prop.X + prop.Len - exist.X + prop.Len -= delta + if prop.Len <= 0 { + return empty, prop + } + } + if exist.X <= prop.X && prop.X < exist.X+exist.Len { + // remove the beginning of prop where overlaps with exist + delta := exist.X + exist.Len - prop.X + prop.Len -= delta + if prop.Len <= 0 { + return empty, prop + } + prop.X += delta + prop.Y += delta + } + if prop.Y <= exist.Y && exist.Y < prop.Y+prop.Len { + // remove the end of prop that overlaps (in Y) with exist + delta := prop.Y + prop.Len - exist.Y + prop.Len -= delta + if prop.Len <= 0 { + return empty, prop + } + } + if exist.Y <= prop.Y && prop.Y < exist.Y+exist.Len { + // remove the beginning of peop that overlaps with exist + delta := exist.Y + exist.Len - prop.Y + prop.Len -= delta + if prop.Len <= 0 { + return empty, prop + } + prop.X += delta // no test reaches this code + prop.Y += delta + } + if prop.X+prop.Len <= exist.X && prop.Y+prop.Len <= exist.Y { + return leftdown, prop + } + if exist.X+exist.Len <= prop.X && exist.Y+exist.Len <= prop.Y { + return rightup, prop + } + // prop can't be in an lcs that contains exist + return bad, prop +} + +// manipulating Diag and lcs + +// prependlcs a diagonal (x,y)-(x+1,y+1) segment either to an empty lcs +// or to its first Diag. prependlcs is only called extending diagonals +// the backward direction. +func prependlcs(lcs lcs, x, y int) lcs { + if len(lcs) > 0 { + d := &lcs[0] + if int(d.X) == x+1 && int(d.Y) == y+1 { + // extend the diagonal down and to the left + d.X, d.Y = int(x), int(y) + d.Len++ + return lcs + } + } + + r := diag{X: int(x), Y: int(y), Len: 1} + lcs = append([]diag{r}, lcs...) + return lcs +} + +// appendlcs appends a diagonal, or extends the existing one. +// by adding the edge (x,y)-(x+1.y+1). appendlcs is only called +// while extending diagonals in the forward direction. +func appendlcs(lcs lcs, x, y int) lcs { + if len(lcs) > 0 { + last := &lcs[len(lcs)-1] + // Expand last element if adjoining. + if last.X+last.Len == x && last.Y+last.Len == y { + last.Len++ + return lcs + } + } + + return append(lcs, diag{X: x, Y: y, Len: 1}) +} + +// enforce constraint on d, k +func ok(d, k int) bool { + return d >= 0 && -d <= k && k <= d +} + +type Diff struct { + Start, End int // offsets in A + Text string // replacement text +} diff --git a/gopls/golang_org_x_tools/diff/lcs/doc.go b/gopls/golang_org_x_tools/diff/lcs/doc.go new file mode 100644 index 0000000..dc779f3 --- /dev/null +++ b/gopls/golang_org_x_tools/diff/lcs/doc.go @@ -0,0 +1,156 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// package lcs contains code to find longest-common-subsequences +// (and diffs) +package lcs + +/* +Compute longest-common-subsequences of two slices A, B using +algorithms from Myers' paper. A longest-common-subsequence +(LCS from now on) of A and B is a maximal set of lexically increasing +pairs of subscripts (x,y) with A[x]==B[y]. There may be many LCS, but +they all have the same length. An LCS determines a sequence of edits +that changes A into B. + +The key concept is the edit graph of A and B. +If A has length N and B has length M, then the edit graph has +vertices v[i][j] for 0 <= i <= N, 0 <= j <= M. There is a +horizontal edge from v[i][j] to v[i+1][j] whenever both are in +the graph, and a vertical edge from v[i][j] to f[i][j+1] similarly. +When A[i] == B[j] there is a diagonal edge from v[i][j] to v[i+1][j+1]. + +A path between in the graph between (0,0) and (N,M) determines a sequence +of edits converting A into B: each horizontal edge corresponds to removing +an element of A, and each vertical edge corresponds to inserting an +element of B. + +A vertex (x,y) is on (forward) diagonal k if x-y=k. A path in the graph +is of length D if it has D non-diagonal edges. The algorithms generate +forward paths (in which at least one of x,y increases at each edge), +or backward paths (in which at least one of x,y decreases at each edge), +or a combination. (Note that the orientation is the traditional mathematical one, +with the origin in the lower-left corner.) + +Here is the edit graph for A:"aabbaa", B:"aacaba". (I know the diagonals look weird.) + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + b | | | ___/‾‾‾ | ___/‾‾‾ | | | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + c | | | | | | | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + a a b b a a + + +The algorithm labels a vertex (x,y) with D,k if it is on diagonal k and at +the end of a maximal path of length D. (Because x-y=k it suffices to remember +only the x coordinate of the vertex.) + +The forward algorithm: Find the longest diagonal starting at (0,0) and +label its end with D=0,k=0. From that vertex take a vertical step and +then follow the longest diagonal (up and to the right), and label that vertex +with D=1,k=-1. From the D=0,k=0 point take a horizontal step and the follow +the longest diagonal (up and to the right) and label that vertex +D=1,k=1. In the same way, having labelled all the D vertices, +from a vertex labelled D,k find two vertices +tentatively labelled D+1,k-1 and D+1,k+1. There may be two on the same +diagonal, in which case take the one with the larger x. + +Eventually the path gets to (N,M), and the diagonals on it are the LCS. + +Here is the edit graph with the ends of D-paths labelled. (So, for instance, +0/2,2 indicates that x=2,y=2 is labelled with 0, as it should be, since the first +step is to go up the longest diagonal from (0,0).) +A:"aabbaa", B:"aacaba" + ⊙ ------- ⊙ ------- ⊙ -------(3/3,6)------- ⊙ -------(3/5,6)-------(4/6,6) + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ ------- ⊙ -------(2/3,5)------- ⊙ ------- ⊙ ------- ⊙ + b | | | ___/‾‾‾ | ___/‾‾‾ | | | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ -------(3/5,4)------- ⊙ + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ -------(1/2,3)-------(2/3,3)------- ⊙ ------- ⊙ ------- ⊙ + c | | | | | | | + ⊙ ------- ⊙ -------(0/2,2)-------(1/3,2)-------(2/4,2)-------(3/5,2)-------(4/6,2) + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + a | ___/‾‾‾ | ___/‾‾‾ | | | ___/‾‾‾ | ___/‾‾‾ | + ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ ------- ⊙ + a a b b a a + +The 4-path is reconstructed starting at (4/6,6), horizontal to (3/5,6), diagonal to (3,4), vertical +to (2/3,3), horizontal to (1/2,3), vertical to (0/2,2), and diagonal to (0,0). As expected, +there are 4 non-diagonal steps, and the diagonals form an LCS. + +There is a symmetric backward algorithm, which gives (backwards labels are prefixed with a colon): +A:"aabbaa", B:"aacaba" + ⊙ -------- ⊙ -------- ⊙ -------- ⊙ -------- ⊙ -------- ⊙ -------- ⊙ + a | ____/‾‾‾ | ____/‾‾‾ | | | ____/‾‾‾ | ____/‾‾‾ | + ⊙ -------- ⊙ -------- ⊙ -------- ⊙ -------- ⊙ --------(:0/5,5)-------- ⊙ + b | | | ____/‾‾‾ | ____/‾‾‾ | | | + ⊙ -------- ⊙ -------- ⊙ --------(:1/3,4)-------- ⊙ -------- ⊙ -------- ⊙ + a | ____/‾‾‾ | ____/‾‾‾ | | | ____/‾‾‾ | ____/‾‾‾ | + (:3/0,3)--------(:2/1,3)-------- ⊙ --------(:2/3,3)--------(:1/4,3)-------- ⊙ -------- ⊙ + c | | | | | | | + ⊙ -------- ⊙ -------- ⊙ --------(:3/3,2)--------(:2/4,2)-------- ⊙ -------- ⊙ + a | ____/‾‾‾ | ____/‾‾‾ | | | ____/‾‾‾ | ____/‾‾‾ | + (:3/0,1)-------- ⊙ -------- ⊙ -------- ⊙ --------(:3/4,1)-------- ⊙ -------- ⊙ + a | ____/‾‾‾ | ____/‾‾‾ | | | ____/‾‾‾ | ____/‾‾‾ | + (:4/0,0)-------- ⊙ -------- ⊙ -------- ⊙ --------(:4/4,0)-------- ⊙ -------- ⊙ + a a b b a a + +Neither of these is ideal for use in an editor, where it is undesirable to send very long diffs to the +front end. It's tricky to decide exactly what 'very long diffs' means, as "replace A by B" is very short. +We want to control how big D can be, by stopping when it gets too large. The forward algorithm then +privileges common prefixes, and the backward algorithm privileges common suffixes. Either is an undesirable +asymmetry. + +Fortunately there is a two-sided algorithm, implied by results in Myers' paper. Here's what the labels in +the edit graph look like. +A:"aabbaa", B:"aacaba" + ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ + a | ____/‾‾‾‾ | ____/‾‾‾‾ | | | ____/‾‾‾‾ | ____/‾‾‾‾ | + ⊙ --------- ⊙ --------- ⊙ --------- (2/3,5) --------- ⊙ --------- (:0/5,5)--------- ⊙ + b | | | ____/‾‾‾‾ | ____/‾‾‾‾ | | | + ⊙ --------- ⊙ --------- ⊙ --------- (:1/3,4)--------- ⊙ --------- ⊙ --------- ⊙ + a | ____/‾‾‾‾ | ____/‾‾‾‾ | | | ____/‾‾‾‾ | ____/‾‾‾‾ | + ⊙ --------- (:2/1,3)--------- (1/2,3) ---------(2:2/3,3)--------- (:1/4,3)--------- ⊙ --------- ⊙ + c | | | | | | | + ⊙ --------- ⊙ --------- (0/2,2) --------- (1/3,2) ---------(2:2/4,2)--------- ⊙ --------- ⊙ + a | ____/‾‾‾‾ | ____/‾‾‾‾ | | | ____/‾‾‾‾ | ____/‾‾‾‾ | + ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ + a | ____/‾‾‾‾ | ____/‾‾‾‾ | | | ____/‾‾‾‾ | ____/‾‾‾‾ | + ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ --------- ⊙ + a a b b a a + +The algorithm stopped when it saw the backwards 2-path ending at (1,3) and the forwards 2-path ending at (3,5). The criterion +is a backwards path ending at (u,v) and a forward path ending at (x,y), where u <= x and the two points are on the same +diagonal. (Here the edgegraph has a diagonal, but the criterion is x-y=u-v.) Myers proves there is a forward +2-path from (0,0) to (1,3), and that together with the backwards 2-path ending at (1,3) gives the expected 4-path. +Unfortunately the forward path has to be constructed by another run of the forward algorithm; it can't be found from the +computed labels. That is the worst case. Had the code noticed (x,y)=(u,v)=(3,3) the whole path could be reconstructed +from the edgegraph. The implementation looks for a number of special cases to try to avoid computing an extra forward path. + +If the two-sided algorithm has stop early (because D has become too large) it will have found a forward LCS and a +backwards LCS. Ideally these go with disjoint prefixes and suffixes of A and B, but disjointness may fail and the two +computed LCS may conflict. (An easy example is where A is a suffix of B, and shares a short prefix. The backwards LCS +is all of A, and the forward LCS is a prefix of A.) The algorithm combines the two +to form a best-effort LCS. In the worst case the forward partial LCS may have to +be recomputed. +*/ + +/* Eugene Myers paper is titled +"An O(ND) Difference Algorithm and Its Variations" +and can be found at +http://www.xmailserver.org/diff2.pdf + +(There is a generic implementation of the algorithm the the repository with git hash +b9ad7e4ade3a686d608e44475390ad428e60e7fc) +*/ diff --git a/gopls/golang_org_x_tools/diff/lcs/git.sh b/gopls/golang_org_x_tools/diff/lcs/git.sh new file mode 100644 index 0000000..6856f84 --- /dev/null +++ b/gopls/golang_org_x_tools/diff/lcs/git.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# +# 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. +# +# Creates a zip file containing all numbered versions +# of the commit history of a large source file, for use +# as input data for the tests of the diff algorithm. +# +# Run script from root of the x/tools repo. + +set -eu + +# WARNING: This script will install the latest version of $file +# The largest real source file in the x/tools repo. +# file=internal/lsp/source/completion/completion.go +# file=internal/lsp/source/diagnostics.go +file=internal/lsp/protocol/tsprotocol.go + +tmp=$(mktemp -d) +git log $file | + awk '/^commit / {print $2}' | + nl -ba -nrz | + while read n hash; do + git checkout --quiet $hash $file + cp -f $file $tmp/$n + done +(cd $tmp && zip -q - *) > testdata.zip +rm -fr $tmp +git restore --staged $file +git restore $file +echo "Created testdata.zip" diff --git a/gopls/golang_org_x_tools/diff/lcs/labels.go b/gopls/golang_org_x_tools/diff/lcs/labels.go new file mode 100644 index 0000000..0689f1e --- /dev/null +++ b/gopls/golang_org_x_tools/diff/lcs/labels.go @@ -0,0 +1,55 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lcs + +import ( + "fmt" +) + +// For each D, vec[D] has length D+1, +// and the label for (D, k) is stored in vec[D][(D+k)/2]. +type label struct { + vec [][]int +} + +// Temporary checking DO NOT COMMIT true TO PRODUCTION CODE +const debug = false + +// debugging. check that the (d,k) pair is valid +// (that is, -d<=k<=d and d+k even) +func checkDK(D, k int) { + if k >= -D && k <= D && (D+k)%2 == 0 { + return + } + panic(fmt.Sprintf("out of range, d=%d,k=%d", D, k)) +} + +func (t *label) set(D, k, x int) { + if debug { + checkDK(D, k) + } + for len(t.vec) <= D { + t.vec = append(t.vec, nil) + } + if t.vec[D] == nil { + t.vec[D] = make([]int, D+1) + } + t.vec[D][(D+k)/2] = x // known that D+k is even +} + +func (t *label) get(d, k int) int { + if debug { + checkDK(d, k) + } + return int(t.vec[d][(d+k)/2]) +} + +func newtriang(limit int) label { + if limit < 100 { + // Preallocate if limit is not large. + return label{vec: make([][]int, limit)} + } + return label{} +} diff --git a/gopls/golang_org_x_tools/diff/lcs/old.go b/gopls/golang_org_x_tools/diff/lcs/old.go new file mode 100644 index 0000000..a091edd --- /dev/null +++ b/gopls/golang_org_x_tools/diff/lcs/old.go @@ -0,0 +1,530 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lcs + +import ( + "fmt" + "strings" +) + +// non generic code. The names have Old at the end to indicate they are the +// the implementation that doesn't use generics. + +// Compute the Diffs and the lcs. +func Compute(a, b interface{}, limit int) ([]Diff, lcs) { + var ans lcs + g := newegraph(a, b, limit) + ans = g.twosided() + diffs := g.fromlcs(ans) + return diffs, ans +} + +// editGraph carries the information for computing the lcs for []byte, []rune, or []string. +type editGraph struct { + eq eq // how to compare elements of A, B, and convert slices to strings + vf, vb label // forward and backward labels + + limit int // maximal value of D + // the bounding rectangle of the current edit graph + lx, ly, ux, uy int + delta int // common subexpression: (ux-lx)-(uy-ly) +} + +// abstraction in place of generic +type eq interface { + eq(i, j int) bool + substr(i, j int) string // string from b[i:j] + lena() int + lenb() int +} + +type byteeq struct { + a, b []byte // the input was ascii. perhaps these could be strings +} + +func (x *byteeq) eq(i, j int) bool { return x.a[i] == x.b[j] } +func (x *byteeq) substr(i, j int) string { return string(x.b[i:j]) } +func (x *byteeq) lena() int { return int(len(x.a)) } +func (x *byteeq) lenb() int { return int(len(x.b)) } + +type runeeq struct { + a, b []rune +} + +func (x *runeeq) eq(i, j int) bool { return x.a[i] == x.b[j] } +func (x *runeeq) substr(i, j int) string { return string(x.b[i:j]) } +func (x *runeeq) lena() int { return int(len(x.a)) } +func (x *runeeq) lenb() int { return int(len(x.b)) } + +type lineeq struct { + a, b []string +} + +func (x *lineeq) eq(i, j int) bool { return x.a[i] == x.b[j] } +func (x *lineeq) substr(i, j int) string { return strings.Join(x.b[i:j], "") } +func (x *lineeq) lena() int { return int(len(x.a)) } +func (x *lineeq) lenb() int { return int(len(x.b)) } + +func neweq(a, b interface{}) eq { + switch x := a.(type) { + case []byte: + return &byteeq{a: x, b: b.([]byte)} + case []rune: + return &runeeq{a: x, b: b.([]rune)} + case []string: + return &lineeq{a: x, b: b.([]string)} + default: + panic(fmt.Sprintf("unexpected type %T in neweq", x)) + } +} + +func (g *editGraph) fromlcs(lcs lcs) []Diff { + var ans []Diff + var pa, pb int // offsets in a, b + for _, l := range lcs { + if pa < l.X && pb < l.Y { + ans = append(ans, Diff{pa, l.X, g.eq.substr(pb, l.Y)}) + } else if pa < l.X { + ans = append(ans, Diff{pa, l.X, ""}) + } else if pb < l.Y { + ans = append(ans, Diff{pa, l.X, g.eq.substr(pb, l.Y)}) + } + pa = l.X + l.Len + pb = l.Y + l.Len + } + if pa < g.eq.lena() && pb < g.eq.lenb() { + ans = append(ans, Diff{pa, g.eq.lena(), g.eq.substr(pb, g.eq.lenb())}) + } else if pa < g.eq.lena() { + ans = append(ans, Diff{pa, g.eq.lena(), ""}) + } else if pb < g.eq.lenb() { + ans = append(ans, Diff{pa, g.eq.lena(), g.eq.substr(pb, g.eq.lenb())}) + } + return ans +} + +func newegraph(a, b interface{}, limit int) *editGraph { + if limit <= 0 { + limit = 1 << 25 // effectively infinity + } + var alen, blen int + switch a := a.(type) { + case []byte: + alen, blen = len(a), len(b.([]byte)) + case []rune: + alen, blen = len(a), len(b.([]rune)) + case []string: + alen, blen = len(a), len(b.([]string)) + default: + panic(fmt.Sprintf("unexpected type %T in newegraph", a)) + } + ans := &editGraph{eq: neweq(a, b), vf: newtriang(limit), vb: newtriang(limit), limit: int(limit), + ux: alen, uy: blen, delta: alen - blen} + return ans +} + +// --- FORWARD --- +// fdone decides if the forwward path has reached the upper right +// corner of the rectangele. If so, it also returns the computed lcs. +func (e *editGraph) fdone(D, k int) (bool, lcs) { + // x, y, k are relative to the rectangle + x := e.vf.get(D, k) + y := x - k + if x == e.ux && y == e.uy { + return true, e.forwardlcs(D, k) + } + return false, nil +} + +// run the forward algorithm, until success or up to the limit on D. +func (e *editGraph) forward() lcs { + e.setForward(0, 0, e.lx) + if ok, ans := e.fdone(0, 0); ok { + return ans + } + // from D to D+1 + for D := 0; D < e.limit; D++ { + e.setForward(D+1, -(D + 1), e.getForward(D, -D)) + if ok, ans := e.fdone(D+1, -(D + 1)); ok { + return ans + } + e.setForward(D+1, D+1, e.getForward(D, D)+1) + if ok, ans := e.fdone(D+1, D+1); ok { + return ans + } + for k := -D + 1; k <= D-1; k += 2 { + // these are tricky and easy to get backwards + lookv := e.lookForward(k, e.getForward(D, k-1)+1) + lookh := e.lookForward(k, e.getForward(D, k+1)) + if lookv > lookh { + e.setForward(D+1, k, lookv) + } else { + e.setForward(D+1, k, lookh) + } + if ok, ans := e.fdone(D+1, k); ok { + return ans + } + } + } + // D is too large + // find the D path with maximal x+y inside the rectangle and + // use that to compute the found part of the lcs + kmax := -e.limit - 1 + diagmax := -1 + for k := -e.limit; k <= e.limit; k += 2 { + x := e.getForward(e.limit, k) + y := x - k + if x+y > diagmax && x <= e.ux && y <= e.uy { + diagmax, kmax = x+y, k + } + } + return e.forwardlcs(e.limit, kmax) +} + +// recover the lcs by backtracking from the farthest point reached +func (e *editGraph) forwardlcs(D, k int) lcs { + var ans lcs + for x := e.getForward(D, k); x != 0 || x-k != 0; { + if ok(D-1, k-1) && x-1 == e.getForward(D-1, k-1) { + // if (x-1,y) is labelled D-1, x--,D--,k--,continue + D, k, x = D-1, k-1, x-1 + continue + } else if ok(D-1, k+1) && x == e.getForward(D-1, k+1) { + // if (x,y-1) is labelled D-1, x, D--,k++, continue + D, k = D-1, k+1 + continue + } + // if (x-1,y-1)--(x,y) is a diagonal, prepend,x--,y--, continue + y := x - k + realx, realy := x+e.lx, y+e.ly + if e.eq.eq(realx-1, realy-1) { + ans = prependlcs(ans, realx-1, realy-1) + x-- + } else { + panic("broken path") + } + } + return ans +} + +// start at (x,y), go up the diagonal as far as possible, +// and label the result with d +func (e *editGraph) lookForward(k, relx int) int { + rely := relx - k + x, y := relx+e.lx, rely+e.ly + for x < e.ux && y < e.uy && e.eq.eq(x, y) { + x++ + y++ + } + return x +} + +func (e *editGraph) setForward(d, k, relx int) { + x := e.lookForward(k, relx) + e.vf.set(d, k, x-e.lx) +} + +func (e *editGraph) getForward(d, k int) int { + x := e.vf.get(d, k) + return x +} + +// --- BACKWARD --- +// bdone decides if the backward path has reached the lower left corner +func (e *editGraph) bdone(D, k int) (bool, lcs) { + // x, y, k are relative to the rectangle + x := e.vb.get(D, k) + y := x - (k + e.delta) + if x == 0 && y == 0 { + return true, e.backwardlcs(D, k) + } + return false, nil +} + +// run the backward algorithm, until success or up to the limit on D. +func (e *editGraph) backward() lcs { + e.setBackward(0, 0, e.ux) + if ok, ans := e.bdone(0, 0); ok { + return ans + } + // from D to D+1 + for D := 0; D < e.limit; D++ { + e.setBackward(D+1, -(D + 1), e.getBackward(D, -D)-1) + if ok, ans := e.bdone(D+1, -(D + 1)); ok { + return ans + } + e.setBackward(D+1, D+1, e.getBackward(D, D)) + if ok, ans := e.bdone(D+1, D+1); ok { + return ans + } + for k := -D + 1; k <= D-1; k += 2 { + // these are tricky and easy to get wrong + lookv := e.lookBackward(k, e.getBackward(D, k-1)) + lookh := e.lookBackward(k, e.getBackward(D, k+1)-1) + if lookv < lookh { + e.setBackward(D+1, k, lookv) + } else { + e.setBackward(D+1, k, lookh) + } + if ok, ans := e.bdone(D+1, k); ok { + return ans + } + } + } + + // D is too large + // find the D path with minimal x+y inside the rectangle and + // use that to compute the part of the lcs found + kmax := -e.limit - 1 + diagmin := 1 << 25 + for k := -e.limit; k <= e.limit; k += 2 { + x := e.getBackward(e.limit, k) + y := x - (k + e.delta) + if x+y < diagmin && x >= 0 && y >= 0 { + diagmin, kmax = x+y, k + } + } + if kmax < -e.limit { + panic(fmt.Sprintf("no paths when limit=%d?", e.limit)) + } + return e.backwardlcs(e.limit, kmax) +} + +// recover the lcs by backtracking +func (e *editGraph) backwardlcs(D, k int) lcs { + var ans lcs + for x := e.getBackward(D, k); x != e.ux || x-(k+e.delta) != e.uy; { + if ok(D-1, k-1) && x == e.getBackward(D-1, k-1) { + // D--, k--, x unchanged + D, k = D-1, k-1 + continue + } else if ok(D-1, k+1) && x+1 == e.getBackward(D-1, k+1) { + // D--, k++, x++ + D, k, x = D-1, k+1, x+1 + continue + } + y := x - (k + e.delta) + realx, realy := x+e.lx, y+e.ly + if e.eq.eq(realx, realy) { + ans = appendlcs(ans, realx, realy) + x++ + } else { + panic("broken path") + } + } + return ans +} + +// start at (x,y), go down the diagonal as far as possible, +func (e *editGraph) lookBackward(k, relx int) int { + rely := relx - (k + e.delta) // forward k = k + e.delta + x, y := relx+e.lx, rely+e.ly + for x > 0 && y > 0 && e.eq.eq(x-1, y-1) { + x-- + y-- + } + return x +} + +// convert to rectangle, and label the result with d +func (e *editGraph) setBackward(d, k, relx int) { + x := e.lookBackward(k, relx) + e.vb.set(d, k, x-e.lx) +} + +func (e *editGraph) getBackward(d, k int) int { + x := e.vb.get(d, k) + return x +} + +// -- TWOSIDED --- + +func (e *editGraph) twosided() lcs { + // The termination condition could be improved, as either the forward + // or backward pass could succeed before Myers' Lemma applies. + // Aside from questions of efficiency (is the extra testing cost-effective) + // this is more likely to matter when e.limit is reached. + e.setForward(0, 0, e.lx) + e.setBackward(0, 0, e.ux) + + // from D to D+1 + for D := 0; D < e.limit; D++ { + // just finished a backwards pass, so check + if got, ok := e.twoDone(D, D); ok { + return e.twolcs(D, D, got) + } + // do a forwards pass (D to D+1) + e.setForward(D+1, -(D + 1), e.getForward(D, -D)) + e.setForward(D+1, D+1, e.getForward(D, D)+1) + for k := -D + 1; k <= D-1; k += 2 { + // these are tricky and easy to get backwards + lookv := e.lookForward(k, e.getForward(D, k-1)+1) + lookh := e.lookForward(k, e.getForward(D, k+1)) + if lookv > lookh { + e.setForward(D+1, k, lookv) + } else { + e.setForward(D+1, k, lookh) + } + } + // just did a forward pass, so check + if got, ok := e.twoDone(D+1, D); ok { + return e.twolcs(D+1, D, got) + } + // do a backward pass, D to D+1 + e.setBackward(D+1, -(D + 1), e.getBackward(D, -D)-1) + e.setBackward(D+1, D+1, e.getBackward(D, D)) + for k := -D + 1; k <= D-1; k += 2 { + // these are tricky and easy to get wrong + lookv := e.lookBackward(k, e.getBackward(D, k-1)) + lookh := e.lookBackward(k, e.getBackward(D, k+1)-1) + if lookv < lookh { + e.setBackward(D+1, k, lookv) + } else { + e.setBackward(D+1, k, lookh) + } + } + } + + // D too large. combine a forward and backward partial lcs + // first, a forward one + kmax := -e.limit - 1 + diagmax := -1 + for k := -e.limit; k <= e.limit; k += 2 { + x := e.getForward(e.limit, k) + y := x - k + if x+y > diagmax && x <= e.ux && y <= e.uy { + diagmax, kmax = x+y, k + } + } + if kmax < -e.limit { + panic(fmt.Sprintf("no forward paths when limit=%d?", e.limit)) + } + lcs := e.forwardlcs(e.limit, kmax) + // now a backward one + // find the D path with minimal x+y inside the rectangle and + // use that to compute the lcs + diagmin := 1 << 25 // infinity + for k := -e.limit; k <= e.limit; k += 2 { + x := e.getBackward(e.limit, k) + y := x - (k + e.delta) + if x+y < diagmin && x >= 0 && y >= 0 { + diagmin, kmax = x+y, k + } + } + if kmax < -e.limit { + panic(fmt.Sprintf("no backward paths when limit=%d?", e.limit)) + } + lcs = append(lcs, e.backwardlcs(e.limit, kmax)...) + // These may overlap (e.forwardlcs and e.backwardlcs return sorted lcs) + ans := lcs.fix() + return ans +} + +// Does Myers' Lemma apply? +func (e *editGraph) twoDone(df, db int) (int, bool) { + if (df+db+e.delta)%2 != 0 { + return 0, false // diagonals cannot overlap + } + kmin := -db + e.delta + if -df > kmin { + kmin = -df + } + kmax := db + e.delta + if df < kmax { + kmax = df + } + for k := kmin; k <= kmax; k += 2 { + x := e.vf.get(df, k) + u := e.vb.get(db, k-e.delta) + if u <= x { + // is it worth looking at all the other k? + for l := k; l <= kmax; l += 2 { + x := e.vf.get(df, l) + y := x - l + u := e.vb.get(db, l-e.delta) + v := u - l + if x == u || u == 0 || v == 0 || y == e.uy || x == e.ux { + return l, true + } + } + return k, true + } + } + return 0, false +} + +func (e *editGraph) twolcs(df, db, kf int) lcs { + // db==df || db+1==df + x := e.vf.get(df, kf) + y := x - kf + kb := kf - e.delta + u := e.vb.get(db, kb) + v := u - kf + + // Myers proved there is a df-path from (0,0) to (u,v) + // and a db-path from (x,y) to (N,M). + // In the first case the overall path is the forward path + // to (u,v) followed by the backward path to (N,M). + // In the second case the path is the backward path to (x,y) + // followed by the forward path to (x,y) from (0,0). + + // Look for some special cases to avoid computing either of these paths. + if x == u { + // "babaab" "cccaba" + // already patched together + lcs := e.forwardlcs(df, kf) + lcs = append(lcs, e.backwardlcs(db, kb)...) + return lcs.sort() + } + + // is (u-1,v) or (u,v-1) labelled df-1? + // if so, that forward df-1-path plus a horizontal or vertical edge + // is the df-path to (u,v), then plus the db-path to (N,M) + if u > 0 && ok(df-1, u-1-v) && e.vf.get(df-1, u-1-v) == u-1 { + // "aabbab" "cbcabc" + lcs := e.forwardlcs(df-1, u-1-v) + lcs = append(lcs, e.backwardlcs(db, kb)...) + return lcs.sort() + } + if v > 0 && ok(df-1, (u-(v-1))) && e.vf.get(df-1, u-(v-1)) == u { + // "abaabb" "bcacab" + lcs := e.forwardlcs(df-1, u-(v-1)) + lcs = append(lcs, e.backwardlcs(db, kb)...) + return lcs.sort() + } + + // The path can't possibly contribute to the lcs because it + // is all horizontal or vertical edges + if u == 0 || v == 0 || x == e.ux || y == e.uy { + // "abaabb" "abaaaa" + if u == 0 || v == 0 { + return e.backwardlcs(db, kb) + } + return e.forwardlcs(df, kf) + } + + // is (x+1,y) or (x,y+1) labelled db-1? + if x+1 <= e.ux && ok(db-1, x+1-y-e.delta) && e.vb.get(db-1, x+1-y-e.delta) == x+1 { + // "bababb" "baaabb" + lcs := e.backwardlcs(db-1, kb+1) + lcs = append(lcs, e.forwardlcs(df, kf)...) + return lcs.sort() + } + if y+1 <= e.uy && ok(db-1, x-(y+1)-e.delta) && e.vb.get(db-1, x-(y+1)-e.delta) == x { + // "abbbaa" "cabacc" + lcs := e.backwardlcs(db-1, kb-1) + lcs = append(lcs, e.forwardlcs(df, kf)...) + return lcs.sort() + } + + // need to compute another path + // "aabbaa" "aacaba" + lcs := e.backwardlcs(db, kb) + oldx, oldy := e.ux, e.uy + e.ux = u + e.uy = v + lcs = append(lcs, e.forward()...) + e.ux, e.uy = oldx, oldy + return lcs.sort() +} diff --git a/gopls/golang_org_x_tools/diff/myers/diff.go b/gopls/golang_org_x_tools/diff/myers/diff.go new file mode 100644 index 0000000..012db48 --- /dev/null +++ b/gopls/golang_org_x_tools/diff/myers/diff.go @@ -0,0 +1,215 @@ +// 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 myers implements the Myers diff algorithm. +package myers + +import ( + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/diff" +) + +// Sources: +// https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/ +// https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2 + +func ComputeEdits(before, after string) []diff.Edit { + beforeLines := splitLines(before) + ops := operations(beforeLines, splitLines(after)) + + // Build a table mapping line number to offset. + lineOffsets := make([]int, 0, len(beforeLines)+1) + total := 0 + for i := range beforeLines { + lineOffsets = append(lineOffsets, total) + total += len(beforeLines[i]) + } + lineOffsets = append(lineOffsets, total) // EOF + + edits := make([]diff.Edit, 0, len(ops)) + for _, op := range ops { + start, end := lineOffsets[op.I1], lineOffsets[op.I2] + switch op.Kind { + case diff.Delete: + // Delete: before[I1:I2] is deleted. + edits = append(edits, diff.Edit{Start: start, End: end}) + case diff.Insert: + // Insert: after[J1:J2] is inserted at before[I1:I1]. + if content := strings.Join(op.Content, ""); content != "" { + edits = append(edits, diff.Edit{Start: start, End: end, New: content}) + } + } + } + return edits +} + +type operation struct { + Kind diff.OpKind + Content []string // content from b + I1, I2 int // indices of the line in a + J1 int // indices of the line in b, J2 implied by len(Content) +} + +// operations returns the list of operations to convert a into b, consolidating +// operations for multiple lines and not including equal lines. +func operations(a, b []string) []*operation { + if len(a) == 0 && len(b) == 0 { + return nil + } + + trace, offset := shortestEditSequence(a, b) + snakes := backtrack(trace, len(a), len(b), offset) + + M, N := len(a), len(b) + + var i int + solution := make([]*operation, len(a)+len(b)) + + add := func(op *operation, i2, j2 int) { + if op == nil { + return + } + op.I2 = i2 + if op.Kind == diff.Insert { + op.Content = b[op.J1:j2] + } + solution[i] = op + i++ + } + x, y := 0, 0 + for _, snake := range snakes { + if len(snake) < 2 { + continue + } + var op *operation + // delete (horizontal) + for snake[0]-snake[1] > x-y { + if op == nil { + op = &operation{ + Kind: diff.Delete, + I1: x, + J1: y, + } + } + x++ + if x == M { + break + } + } + add(op, x, y) + op = nil + // insert (vertical) + for snake[0]-snake[1] < x-y { + if op == nil { + op = &operation{ + Kind: diff.Insert, + I1: x, + J1: y, + } + } + y++ + } + add(op, x, y) + op = nil + // equal (diagonal) + for x < snake[0] { + x++ + y++ + } + if x >= M && y >= N { + break + } + } + return solution[:i] +} + +// backtrack uses the trace for the edit sequence computation and returns the +// "snakes" that make up the solution. A "snake" is a single deletion or +// insertion followed by zero or diagonals. +func backtrack(trace [][]int, x, y, offset int) [][]int { + snakes := make([][]int, len(trace)) + d := len(trace) - 1 + for ; x > 0 && y > 0 && d > 0; d-- { + V := trace[d] + if len(V) == 0 { + continue + } + snakes[d] = []int{x, y} + + k := x - y + + var kPrev int + if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) { + kPrev = k + 1 + } else { + kPrev = k - 1 + } + + x = V[kPrev+offset] + y = x - kPrev + } + if x < 0 || y < 0 { + return snakes + } + snakes[d] = []int{x, y} + return snakes +} + +// shortestEditSequence returns the shortest edit sequence that converts a into b. +func shortestEditSequence(a, b []string) ([][]int, int) { + M, N := len(a), len(b) + V := make([]int, 2*(N+M)+1) + offset := N + M + trace := make([][]int, N+M+1) + + // Iterate through the maximum possible length of the SES (N+M). + for d := 0; d <= N+M; d++ { + copyV := make([]int, len(V)) + // k lines are represented by the equation y = x - k. We move in + // increments of 2 because end points for even d are on even k lines. + for k := -d; k <= d; k += 2 { + // At each point, we either go down or to the right. We go down if + // k == -d, and we go to the right if k == d. We also prioritize + // the maximum x value, because we prefer deletions to insertions. + var x int + if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) { + x = V[k+1+offset] // down + } else { + x = V[k-1+offset] + 1 // right + } + + y := x - k + + // Diagonal moves while we have equal contents. + for x < M && y < N && a[x] == b[y] { + x++ + y++ + } + + V[k+offset] = x + + // Return if we've exceeded the maximum values. + if x == M && y == N { + // Makes sure to save the state of the array before returning. + copy(copyV, V) + trace[d] = copyV + return trace, offset + } + } + + // Save the state of the array. + copy(copyV, V) + trace[d] = copyV + } + return nil, 0 +} + +func splitLines(text string) []string { + lines := strings.SplitAfter(text, "\n") + if lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } + return lines +} diff --git a/gopls/golang_org_x_tools/diff/ndiff.go b/gopls/golang_org_x_tools/diff/ndiff.go new file mode 100644 index 0000000..1a792fb --- /dev/null +++ b/gopls/golang_org_x_tools/diff/ndiff.go @@ -0,0 +1,112 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package diff + +import ( + "bytes" + "unicode/utf8" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/diff/lcs" +) + +// Strings computes the differences between two strings. +// The resulting edits respect rune boundaries. +func Strings(before, after string) []Edit { + if before == after { + return nil // common case + } + + if stringIsASCII(before) && stringIsASCII(after) { + return diffASCII([]byte(before), []byte(after)) + } + return diffRunes([]rune(before), []rune(after)) +} + +// Bytes computes the differences between two byte slices. +// The resulting edits respect rune boundaries. +func Bytes(before, after []byte) []Edit { + if bytes.Equal(before, after) { + return nil // common case + } + + if bytesIsASCII(before) && bytesIsASCII(after) { + return diffASCII(before, after) + } + return diffRunes(runes(before), runes(after)) +} + +func diffASCII(before, after []byte) []Edit { + diffs, _ := lcs.Compute(before, after, maxDiffs/2) + + // Convert from LCS diffs. + res := make([]Edit, len(diffs)) + for i, d := range diffs { + res[i] = Edit{d.Start, d.End, d.Text} + } + return res +} + +func diffRunes(before, after []rune) []Edit { + diffs, _ := lcs.Compute(before, after, maxDiffs/2) + + // The diffs returned by the lcs package use indexes + // into whatever slice was passed in. + // Convert rune offsets to byte offsets. + res := make([]Edit, len(diffs)) + lastEnd := 0 + utf8Len := 0 + for i, d := range diffs { + utf8Len += runesLen(before[lastEnd:d.Start]) // text between edits + start := utf8Len + utf8Len += runesLen(before[d.Start:d.End]) // text deleted by this edit + res[i] = Edit{start, utf8Len, d.Text} + lastEnd = d.End + } + return res +} + +// maxDiffs is a limit on how deeply the lcs algorithm should search +// the value is just a guess +const maxDiffs = 30 + +// runes is like []rune(string(bytes)) without the duplicate allocation. +func runes(bytes []byte) []rune { + n := utf8.RuneCount(bytes) + runes := make([]rune, n) + for i := 0; i < n; i++ { + r, sz := utf8.DecodeRune(bytes) + bytes = bytes[sz:] + runes[i] = r + } + return runes +} + +// runesLen returns the length in bytes of the UTF-8 encoding of runes. +func runesLen(runes []rune) (len int) { + for _, r := range runes { + len += utf8.RuneLen(r) + } + return len +} + +// stringIsASCII reports whether s contains only ASCII. +// TODO(adonovan): combine when x/tools allows generics. +func stringIsASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] >= utf8.RuneSelf { + return false + } + } + return true +} + +func bytesIsASCII(s []byte) bool { + for i := 0; i < len(s); i++ { + if s[i] >= utf8.RuneSelf { + return false + } + } + return true +} diff --git a/gopls/golang_org_x_tools/diff/unified.go b/gopls/golang_org_x_tools/diff/unified.go new file mode 100644 index 0000000..fa376f1 --- /dev/null +++ b/gopls/golang_org_x_tools/diff/unified.go @@ -0,0 +1,248 @@ +// 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 diff + +import ( + "fmt" + "log" + "strings" +) + +// Unified returns a unified diff of the old and new strings. +// The old and new labels are the names of the old and new files. +// If the strings are equal, it returns the empty string. +func Unified(oldLabel, newLabel, old, new string) string { + edits := Strings(old, new) + unified, err := ToUnified(oldLabel, newLabel, old, edits) + if err != nil { + // Can't happen: edits are consistent. + log.Fatalf("internal error in diff.Unified: %v", err) + } + return unified +} + +// ToUnified applies the edits to content and returns a unified diff. +// The old and new labels are the names of the content and result files. +// It returns an error if the edits are inconsistent; see ApplyEdits. +func ToUnified(oldLabel, newLabel, content string, edits []Edit) (string, error) { + u, err := toUnified(oldLabel, newLabel, content, edits) + if err != nil { + return "", err + } + return u.String(), nil +} + +// unified represents a set of edits as a unified diff. +type unified struct { + // From is the name of the original file. + From string + // To is the name of the modified file. + To string + // Hunks is the set of edit hunks needed to transform the file content. + Hunks []*hunk +} + +// Hunk represents a contiguous set of line edits to apply. +type hunk struct { + // The line in the original source where the hunk starts. + FromLine int + // The line in the original source where the hunk finishes. + ToLine int + // The set of line based edits to apply. + Lines []line +} + +// Line represents a single line operation to apply as part of a Hunk. +type line struct { + // Kind is the type of line this represents, deletion, insertion or copy. + Kind OpKind + // Content is the content of this line. + // For deletion it is the line being removed, for all others it is the line + // to put in the output. + Content string +} + +// OpKind is used to denote the type of operation a line represents. +// TODO(adonovan): hide this once the myers package no longer references it. +type OpKind int + +const ( + // Delete is the operation kind for a line that is present in the input + // but not in the output. + Delete OpKind = iota + // Insert is the operation kind for a line that is new in the output. + Insert + // Equal is the operation kind for a line that is the same in the input and + // output, often used to provide context around edited lines. + Equal +) + +// String returns a human readable representation of an OpKind. It is not +// intended for machine processing. +func (k OpKind) String() string { + switch k { + case Delete: + return "delete" + case Insert: + return "insert" + case Equal: + return "equal" + default: + panic("unknown operation kind") + } +} + +const ( + edge = 3 + gap = edge * 2 +) + +// toUnified takes a file contents and a sequence of edits, and calculates +// a unified diff that represents those edits. +func toUnified(fromName, toName string, content string, edits []Edit) (unified, error) { + u := unified{ + From: fromName, + To: toName, + } + if len(edits) == 0 { + return u, nil + } + var err error + edits, err = lineEdits(content, edits) // expand to whole lines + if err != nil { + return u, err + } + lines := splitLines(content) + var h *hunk + last := 0 + toLine := 0 + for _, edit := range edits { + // Compute the zero-based line numbers of the edit start and end. + // TODO(adonovan): opt: compute incrementally, avoid O(n^2). + start := strings.Count(content[:edit.Start], "\n") + end := strings.Count(content[:edit.End], "\n") + if edit.End == len(content) && len(content) > 0 && content[len(content)-1] != '\n' { + end++ // EOF counts as an implicit newline + } + + switch { + case h != nil && start == last: + //direct extension + case h != nil && start <= last+gap: + //within range of previous lines, add the joiners + addEqualLines(h, lines, last, start) + default: + //need to start a new hunk + if h != nil { + // add the edge to the previous hunk + addEqualLines(h, lines, last, last+edge) + u.Hunks = append(u.Hunks, h) + } + toLine += start - last + h = &hunk{ + FromLine: start + 1, + ToLine: toLine + 1, + } + // add the edge to the new hunk + delta := addEqualLines(h, lines, start-edge, start) + h.FromLine -= delta + h.ToLine -= delta + } + last = start + for i := start; i < end; i++ { + h.Lines = append(h.Lines, line{Kind: Delete, Content: lines[i]}) + last++ + } + if edit.New != "" { + for _, content := range splitLines(edit.New) { + h.Lines = append(h.Lines, line{Kind: Insert, Content: content}) + toLine++ + } + } + } + if h != nil { + // add the edge to the final hunk + addEqualLines(h, lines, last, last+edge) + u.Hunks = append(u.Hunks, h) + } + return u, nil +} + +func splitLines(text string) []string { + lines := strings.SplitAfter(text, "\n") + if lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } + return lines +} + +func addEqualLines(h *hunk, lines []string, start, end int) int { + delta := 0 + for i := start; i < end; i++ { + if i < 0 { + continue + } + if i >= len(lines) { + return delta + } + h.Lines = append(h.Lines, line{Kind: Equal, Content: lines[i]}) + delta++ + } + return delta +} + +// String converts a unified diff to the standard textual form for that diff. +// The output of this function can be passed to tools like patch. +func (u unified) String() string { + if len(u.Hunks) == 0 { + return "" + } + b := new(strings.Builder) + fmt.Fprintf(b, "--- %s\n", u.From) + fmt.Fprintf(b, "+++ %s\n", u.To) + for _, hunk := range u.Hunks { + fromCount, toCount := 0, 0 + for _, l := range hunk.Lines { + switch l.Kind { + case Delete: + fromCount++ + case Insert: + toCount++ + default: + fromCount++ + toCount++ + } + } + fmt.Fprint(b, "@@") + if fromCount > 1 { + fmt.Fprintf(b, " -%d,%d", hunk.FromLine, fromCount) + } else if hunk.FromLine == 1 && fromCount == 0 { + // Match odd GNU diff -u behavior adding to empty file. + fmt.Fprintf(b, " -0,0") + } else { + fmt.Fprintf(b, " -%d", hunk.FromLine) + } + if toCount > 1 { + fmt.Fprintf(b, " +%d,%d", hunk.ToLine, toCount) + } else { + fmt.Fprintf(b, " +%d", hunk.ToLine) + } + fmt.Fprint(b, " @@\n") + for _, l := range hunk.Lines { + switch l.Kind { + case Delete: + fmt.Fprintf(b, "-%s", l.Content) + case Insert: + fmt.Fprintf(b, "+%s", l.Content) + default: + fmt.Fprintf(b, " %s", l.Content) + } + if !strings.HasSuffix(l.Content, "\n") { + fmt.Fprintf(b, "\n\\ No newline at end of file\n") + } + } + } + return b.String() +} diff --git a/gopls/golang_org_x_tools/event/core/event.go b/gopls/golang_org_x_tools/event/core/event.go new file mode 100644 index 0000000..8addaa7 --- /dev/null +++ b/gopls/golang_org_x_tools/event/core/event.go @@ -0,0 +1,85 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package core provides support for event based telemetry. +package core + +import ( + "fmt" + "time" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +// Event holds the information about an event of note that occurred. +type Event struct { + at time.Time + + // As events are often on the stack, storing the first few labels directly + // in the event can avoid an allocation at all for the very common cases of + // simple events. + // The length needs to be large enough to cope with the majority of events + // but no so large as to cause undue stack pressure. + // A log message with two values will use 3 labels (one for each value and + // one for the message itself). + + static [3]label.Label // inline storage for the first few labels + dynamic []label.Label // dynamically sized storage for remaining labels +} + +// eventLabelMap implements label.Map for a the labels of an Event. +type eventLabelMap struct { + event Event +} + +func (ev Event) At() time.Time { return ev.at } + +func (ev Event) Format(f fmt.State, r rune) { + if !ev.at.IsZero() { + fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 ")) + } + for index := 0; ev.Valid(index); index++ { + if l := ev.Label(index); l.Valid() { + fmt.Fprintf(f, "\n\t%v", l) + } + } +} + +func (ev Event) Valid(index int) bool { + return index >= 0 && index < len(ev.static)+len(ev.dynamic) +} + +func (ev Event) Label(index int) label.Label { + if index < len(ev.static) { + return ev.static[index] + } + return ev.dynamic[index-len(ev.static)] +} + +func (ev Event) Find(key label.Key) label.Label { + for _, l := range ev.static { + if l.Key() == key { + return l + } + } + for _, l := range ev.dynamic { + if l.Key() == key { + return l + } + } + return label.Label{} +} + +func MakeEvent(static [3]label.Label, labels []label.Label) Event { + return Event{ + static: static, + dynamic: labels, + } +} + +// CloneEvent event returns a copy of the event with the time adjusted to at. +func CloneEvent(ev Event, at time.Time) Event { + ev.at = at + return ev +} diff --git a/gopls/golang_org_x_tools/event/core/export.go b/gopls/golang_org_x_tools/event/core/export.go new file mode 100644 index 0000000..a482a57 --- /dev/null +++ b/gopls/golang_org_x_tools/event/core/export.go @@ -0,0 +1,70 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package core + +import ( + "context" + "sync/atomic" + "time" + "unsafe" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +// Exporter is a function that handles events. +// It may return a modified context and event. +type Exporter func(context.Context, Event, label.Map) context.Context + +var ( + exporter unsafe.Pointer +) + +// SetExporter sets the global exporter function that handles all events. +// The exporter is called synchronously from the event call site, so it should +// return quickly so as not to hold up user code. +func SetExporter(e Exporter) { + p := unsafe.Pointer(&e) + if e == nil { + // &e is always valid, and so p is always valid, but for the early abort + // of ProcessEvent to be efficient it needs to make the nil check on the + // pointer without having to dereference it, so we make the nil function + // also a nil pointer + p = nil + } + atomic.StorePointer(&exporter, p) +} + +// deliver is called to deliver an event to the supplied exporter. +// it will fill in the time. +func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context { + // add the current time to the event + ev.at = time.Now() + // hand the event off to the current exporter + return exporter(ctx, ev, ev) +} + +// Export is called to deliver an event to the global exporter if set. +func Export(ctx context.Context, ev Event) context.Context { + // get the global exporter and abort early if there is not one + exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) + if exporterPtr == nil { + return ctx + } + return deliver(ctx, *exporterPtr, ev) +} + +// ExportPair is called to deliver a start event to the supplied exporter. +// It also returns a function that will deliver the end event to the same +// exporter. +// It will fill in the time. +func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) { + // get the global exporter and abort early if there is not one + exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) + if exporterPtr == nil { + return ctx, func() {} + } + ctx = deliver(ctx, *exporterPtr, begin) + return ctx, func() { deliver(ctx, *exporterPtr, end) } +} diff --git a/gopls/golang_org_x_tools/event/core/fast.go b/gopls/golang_org_x_tools/event/core/fast.go new file mode 100644 index 0000000..d293da7 --- /dev/null +++ b/gopls/golang_org_x_tools/event/core/fast.go @@ -0,0 +1,77 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package core + +import ( + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/keys" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +// Log1 takes a message and one label delivers a log event to the exporter. +// It is a customized version of Print that is faster and does no allocation. +func Log1(ctx context.Context, message string, t1 label.Label) { + Export(ctx, MakeEvent([3]label.Label{ + keys.Msg.Of(message), + t1, + }, nil)) +} + +// Log2 takes a message and two labels and delivers a log event to the exporter. +// It is a customized version of Print that is faster and does no allocation. +func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) { + Export(ctx, MakeEvent([3]label.Label{ + keys.Msg.Of(message), + t1, + t2, + }, nil)) +} + +// Metric1 sends a label event to the exporter with the supplied labels. +func Metric1(ctx context.Context, t1 label.Label) context.Context { + return Export(ctx, MakeEvent([3]label.Label{ + keys.Metric.New(), + t1, + }, nil)) +} + +// Metric2 sends a label event to the exporter with the supplied labels. +func Metric2(ctx context.Context, t1, t2 label.Label) context.Context { + return Export(ctx, MakeEvent([3]label.Label{ + keys.Metric.New(), + t1, + t2, + }, nil)) +} + +// Start1 sends a span start event with the supplied label list to the exporter. +// It also returns a function that will end the span, which should normally be +// deferred. +func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) { + return ExportPair(ctx, + MakeEvent([3]label.Label{ + keys.Start.Of(name), + t1, + }, nil), + MakeEvent([3]label.Label{ + keys.End.New(), + }, nil)) +} + +// Start2 sends a span start event with the supplied label list to the exporter. +// It also returns a function that will end the span, which should normally be +// deferred. +func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) { + return ExportPair(ctx, + MakeEvent([3]label.Label{ + keys.Start.Of(name), + t1, + t2, + }, nil), + MakeEvent([3]label.Label{ + keys.End.New(), + }, nil)) +} diff --git a/gopls/golang_org_x_tools/event/doc.go b/gopls/golang_org_x_tools/event/doc.go new file mode 100644 index 0000000..5dc6e6b --- /dev/null +++ b/gopls/golang_org_x_tools/event/doc.go @@ -0,0 +1,7 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package event provides a set of packages that cover the main +// concepts of telemetry in an implementation agnostic way. +package event diff --git a/gopls/golang_org_x_tools/event/event.go b/gopls/golang_org_x_tools/event/event.go new file mode 100644 index 0000000..ace6b76 --- /dev/null +++ b/gopls/golang_org_x_tools/event/event.go @@ -0,0 +1,127 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package event + +import ( + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/core" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/keys" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +// Exporter is a function that handles events. +// It may return a modified context and event. +type Exporter func(context.Context, core.Event, label.Map) context.Context + +// SetExporter sets the global exporter function that handles all events. +// The exporter is called synchronously from the event call site, so it should +// return quickly so as not to hold up user code. +func SetExporter(e Exporter) { + core.SetExporter(core.Exporter(e)) +} + +// Log takes a message and a label list and combines them into a single event +// before delivering them to the exporter. +func Log(ctx context.Context, message string, labels ...label.Label) { + core.Export(ctx, core.MakeEvent([3]label.Label{ + keys.Msg.Of(message), + }, labels)) +} + +// IsLog returns true if the event was built by the Log function. +// It is intended to be used in exporters to identify the semantics of the +// event when deciding what to do with it. +func IsLog(ev core.Event) bool { + return ev.Label(0).Key() == keys.Msg +} + +// Error takes a message and a label list and combines them into a single event +// before delivering them to the exporter. It captures the error in the +// delivered event. +func Error(ctx context.Context, message string, err error, labels ...label.Label) { + core.Export(ctx, core.MakeEvent([3]label.Label{ + keys.Msg.Of(message), + keys.Err.Of(err), + }, labels)) +} + +// IsError returns true if the event was built by the Error function. +// It is intended to be used in exporters to identify the semantics of the +// event when deciding what to do with it. +func IsError(ev core.Event) bool { + return ev.Label(0).Key() == keys.Msg && + ev.Label(1).Key() == keys.Err +} + +// Metric sends a label event to the exporter with the supplied labels. +func Metric(ctx context.Context, labels ...label.Label) { + core.Export(ctx, core.MakeEvent([3]label.Label{ + keys.Metric.New(), + }, labels)) +} + +// IsMetric returns true if the event was built by the Metric function. +// It is intended to be used in exporters to identify the semantics of the +// event when deciding what to do with it. +func IsMetric(ev core.Event) bool { + return ev.Label(0).Key() == keys.Metric +} + +// Label sends a label event to the exporter with the supplied labels. +func Label(ctx context.Context, labels ...label.Label) context.Context { + return core.Export(ctx, core.MakeEvent([3]label.Label{ + keys.Label.New(), + }, labels)) +} + +// IsLabel returns true if the event was built by the Label function. +// It is intended to be used in exporters to identify the semantics of the +// event when deciding what to do with it. +func IsLabel(ev core.Event) bool { + return ev.Label(0).Key() == keys.Label +} + +// Start sends a span start event with the supplied label list to the exporter. +// It also returns a function that will end the span, which should normally be +// deferred. +func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) { + return core.ExportPair(ctx, + core.MakeEvent([3]label.Label{ + keys.Start.Of(name), + }, labels), + core.MakeEvent([3]label.Label{ + keys.End.New(), + }, nil)) +} + +// IsStart returns true if the event was built by the Start function. +// It is intended to be used in exporters to identify the semantics of the +// event when deciding what to do with it. +func IsStart(ev core.Event) bool { + return ev.Label(0).Key() == keys.Start +} + +// IsEnd returns true if the event was built by the End function. +// It is intended to be used in exporters to identify the semantics of the +// event when deciding what to do with it. +func IsEnd(ev core.Event) bool { + return ev.Label(0).Key() == keys.End +} + +// Detach returns a context without an associated span. +// This allows the creation of spans that are not children of the current span. +func Detach(ctx context.Context) context.Context { + return core.Export(ctx, core.MakeEvent([3]label.Label{ + keys.Detach.New(), + }, nil)) +} + +// IsDetach returns true if the event was built by the Detach function. +// It is intended to be used in exporters to identify the semantics of the +// event when deciding what to do with it. +func IsDetach(ev core.Event) bool { + return ev.Label(0).Key() == keys.Detach +} diff --git a/gopls/golang_org_x_tools/event/export/id.go b/gopls/golang_org_x_tools/event/export/id.go new file mode 100644 index 0000000..bf9938b --- /dev/null +++ b/gopls/golang_org_x_tools/event/export/id.go @@ -0,0 +1,71 @@ +// 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 export + +import ( + crand "crypto/rand" + "encoding/binary" + "fmt" + "math/rand" + "sync" + "sync/atomic" +) + +type TraceID [16]byte +type SpanID [8]byte + +func (t TraceID) String() string { + return fmt.Sprintf("%02x", t[:]) +} + +func (s SpanID) String() string { + return fmt.Sprintf("%02x", s[:]) +} + +func (s SpanID) IsValid() bool { + return s != SpanID{} +} + +var ( + generationMu sync.Mutex + nextSpanID uint64 + spanIDInc uint64 + + traceIDAdd [2]uint64 + traceIDRand *rand.Rand +) + +func initGenerator() { + var rngSeed int64 + for _, p := range []interface{}{ + &rngSeed, &traceIDAdd, &nextSpanID, &spanIDInc, + } { + binary.Read(crand.Reader, binary.LittleEndian, p) + } + traceIDRand = rand.New(rand.NewSource(rngSeed)) + spanIDInc |= 1 +} + +func newTraceID() TraceID { + generationMu.Lock() + defer generationMu.Unlock() + if traceIDRand == nil { + initGenerator() + } + var tid [16]byte + binary.LittleEndian.PutUint64(tid[0:8], traceIDRand.Uint64()+traceIDAdd[0]) + binary.LittleEndian.PutUint64(tid[8:16], traceIDRand.Uint64()+traceIDAdd[1]) + return tid +} + +func newSpanID() SpanID { + var id uint64 + for id == 0 { + id = atomic.AddUint64(&nextSpanID, spanIDInc) + } + var sid [8]byte + binary.LittleEndian.PutUint64(sid[:], id) + return sid +} diff --git a/gopls/golang_org_x_tools/event/export/log.go b/gopls/golang_org_x_tools/event/export/log.go new file mode 100644 index 0000000..00b41a7 --- /dev/null +++ b/gopls/golang_org_x_tools/event/export/log.go @@ -0,0 +1,57 @@ +// 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 export + +import ( + "context" + "fmt" + "io" + "sync" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/core" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +// LogWriter returns an Exporter that logs events to the supplied writer. +// If onlyErrors is true it does not log any event that did not have an +// associated error. +// It ignores all telemetry other than log events. +func LogWriter(w io.Writer, onlyErrors bool) event.Exporter { + lw := &logWriter{writer: w, onlyErrors: onlyErrors} + return lw.ProcessEvent +} + +type logWriter struct { + mu sync.Mutex + printer Printer + writer io.Writer + onlyErrors bool +} + +func (w *logWriter) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context { + switch { + case event.IsLog(ev): + if w.onlyErrors && !event.IsError(ev) { + return ctx + } + w.mu.Lock() + defer w.mu.Unlock() + w.printer.WriteEvent(w.writer, ev, lm) + + case event.IsStart(ev): + if span := GetSpan(ctx); span != nil { + fmt.Fprintf(w.writer, "start: %v %v", span.Name, span.ID) + if span.ParentID.IsValid() { + fmt.Fprintf(w.writer, "[%v]", span.ParentID) + } + } + case event.IsEnd(ev): + if span := GetSpan(ctx); span != nil { + fmt.Fprintf(w.writer, "finish: %v %v", span.Name, span.ID) + } + } + return ctx +} diff --git a/gopls/golang_org_x_tools/event/export/printer.go b/gopls/golang_org_x_tools/event/export/printer.go new file mode 100644 index 0000000..46004af --- /dev/null +++ b/gopls/golang_org_x_tools/event/export/printer.go @@ -0,0 +1,43 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package export + +import ( + "io" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/core" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/keys" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +type Printer struct { + buffer [128]byte +} + +func (p *Printer) WriteEvent(w io.Writer, ev core.Event, lm label.Map) { + buf := p.buffer[:0] + if !ev.At().IsZero() { + w.Write(ev.At().AppendFormat(buf, "2006/01/02 15:04:05 ")) + } + msg := keys.Msg.Get(lm) + io.WriteString(w, msg) + if err := keys.Err.Get(lm); err != nil { + if msg != "" { + io.WriteString(w, ": ") + } + io.WriteString(w, err.Error()) + } + for index := 0; ev.Valid(index); index++ { + l := ev.Label(index) + if !l.Valid() || l.Key() == keys.Msg || l.Key() == keys.Err { + continue + } + io.WriteString(w, "\n\t") + io.WriteString(w, l.Key().Name()) + io.WriteString(w, "=") + l.Key().Format(w, buf, l) + } + io.WriteString(w, "\n") +} diff --git a/gopls/golang_org_x_tools/event/export/tag.go b/gopls/golang_org_x_tools/event/export/tag.go new file mode 100644 index 0000000..4bc28dd --- /dev/null +++ b/gopls/golang_org_x_tools/event/export/tag.go @@ -0,0 +1,37 @@ +// 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 export + +import ( + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/core" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +// Labels builds an exporter that manipulates the context using the event. +// If the event is type IsLabel or IsStartSpan then it returns a context updated +// with label values from the event. +// For all other event types the event labels will be updated with values from the +// context if they are missing. +func Labels(output event.Exporter) event.Exporter { + return func(ctx context.Context, ev core.Event, lm label.Map) context.Context { + stored, _ := ctx.Value(labelContextKey).(label.Map) + if event.IsLabel(ev) || event.IsStart(ev) { + // update the label map stored in the context + fromEvent := label.Map(ev) + if stored == nil { + stored = fromEvent + } else { + stored = label.MergeMaps(fromEvent, stored) + } + ctx = context.WithValue(ctx, labelContextKey, stored) + } + // add the stored label context to the label map + lm = label.MergeMaps(lm, stored) + return output(ctx, ev, lm) + } +} diff --git a/gopls/golang_org_x_tools/event/export/trace.go b/gopls/golang_org_x_tools/event/export/trace.go new file mode 100644 index 0000000..ee5c2f5 --- /dev/null +++ b/gopls/golang_org_x_tools/event/export/trace.go @@ -0,0 +1,117 @@ +// 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 export + +import ( + "context" + "fmt" + "sync" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/core" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/keys" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +type SpanContext struct { + TraceID TraceID + SpanID SpanID +} + +type Span struct { + Name string + ID SpanContext + ParentID SpanID + mu sync.Mutex + start core.Event + finish core.Event + events []core.Event +} + +type contextKeyType int + +const ( + spanContextKey = contextKeyType(iota) + labelContextKey +) + +func GetSpan(ctx context.Context) *Span { + v := ctx.Value(spanContextKey) + if v == nil { + return nil + } + return v.(*Span) +} + +// Spans creates an exporter that maintains hierarchical span structure in the +// context. +// It creates new spans on start events, adds events to the current span on +// log or label, and closes the span on end events. +// The span structure can then be used by other exporters. +func Spans(output event.Exporter) event.Exporter { + return func(ctx context.Context, ev core.Event, lm label.Map) context.Context { + switch { + case event.IsLog(ev), event.IsLabel(ev): + if span := GetSpan(ctx); span != nil { + span.mu.Lock() + span.events = append(span.events, ev) + span.mu.Unlock() + } + case event.IsStart(ev): + span := &Span{ + Name: keys.Start.Get(lm), + start: ev, + } + if parent := GetSpan(ctx); parent != nil { + span.ID.TraceID = parent.ID.TraceID + span.ParentID = parent.ID.SpanID + } else { + span.ID.TraceID = newTraceID() + } + span.ID.SpanID = newSpanID() + ctx = context.WithValue(ctx, spanContextKey, span) + case event.IsEnd(ev): + if span := GetSpan(ctx); span != nil { + span.mu.Lock() + span.finish = ev + span.mu.Unlock() + } + case event.IsDetach(ev): + ctx = context.WithValue(ctx, spanContextKey, nil) + } + return output(ctx, ev, lm) + } +} + +func (s *SpanContext) Format(f fmt.State, r rune) { + fmt.Fprintf(f, "%v:%v", s.TraceID, s.SpanID) +} + +func (s *Span) Start() core.Event { + // start never changes after construction, so we don't need to hold the mutex + return s.start +} + +func (s *Span) Finish() core.Event { + s.mu.Lock() + defer s.mu.Unlock() + return s.finish +} + +func (s *Span) Events() []core.Event { + s.mu.Lock() + defer s.mu.Unlock() + return s.events +} + +func (s *Span) Format(f fmt.State, r rune) { + s.mu.Lock() + defer s.mu.Unlock() + fmt.Fprintf(f, "%v %v", s.Name, s.ID) + if s.ParentID.IsValid() { + fmt.Fprintf(f, "[%v]", s.ParentID) + } + fmt.Fprintf(f, " %v->%v", s.start, s.finish) +} diff --git a/gopls/golang_org_x_tools/event/keys/keys.go b/gopls/golang_org_x_tools/event/keys/keys.go new file mode 100644 index 0000000..8238419 --- /dev/null +++ b/gopls/golang_org_x_tools/event/keys/keys.go @@ -0,0 +1,564 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package keys + +import ( + "fmt" + "io" + "math" + "strconv" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" +) + +// Value represents a key for untyped values. +type Value struct { + name string + description string +} + +// New creates a new Key for untyped values. +func New(name, description string) *Value { + return &Value{name: name, description: description} +} + +func (k *Value) Name() string { return k.name } +func (k *Value) Description() string { return k.description } + +func (k *Value) Format(w io.Writer, buf []byte, l label.Label) { + fmt.Fprint(w, k.From(l)) +} + +// Get can be used to get a label for the key from a label.Map. +func (k *Value) Get(lm label.Map) interface{} { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return nil +} + +// From can be used to get a value from a Label. +func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() } + +// Of creates a new Label with this key and the supplied value. +func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) } + +// Tag represents a key for tagging labels that have no value. +// These are used when the existence of the label is the entire information it +// carries, such as marking events to be of a specific kind, or from a specific +// package. +type Tag struct { + name string + description string +} + +// NewTag creates a new Key for tagging labels. +func NewTag(name, description string) *Tag { + return &Tag{name: name, description: description} +} + +func (k *Tag) Name() string { return k.name } +func (k *Tag) Description() string { return k.description } + +func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {} + +// New creates a new Label with this key. +func (k *Tag) New() label.Label { return label.OfValue(k, nil) } + +// Int represents a key +type Int struct { + name string + description string +} + +// NewInt creates a new Key for int values. +func NewInt(name, description string) *Int { + return &Int{name: name, description: description} +} + +func (k *Int) Name() string { return k.name } +func (k *Int) Description() string { return k.description } + +func (k *Int) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *Int) Get(lm label.Map) int { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *Int) From(t label.Label) int { return int(t.Unpack64()) } + +// Int8 represents a key +type Int8 struct { + name string + description string +} + +// NewInt8 creates a new Key for int8 values. +func NewInt8(name, description string) *Int8 { + return &Int8{name: name, description: description} +} + +func (k *Int8) Name() string { return k.name } +func (k *Int8) Description() string { return k.description } + +func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *Int8) Get(lm label.Map) int8 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) } + +// Int16 represents a key +type Int16 struct { + name string + description string +} + +// NewInt16 creates a new Key for int16 values. +func NewInt16(name, description string) *Int16 { + return &Int16{name: name, description: description} +} + +func (k *Int16) Name() string { return k.name } +func (k *Int16) Description() string { return k.description } + +func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *Int16) Get(lm label.Map) int16 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) } + +// Int32 represents a key +type Int32 struct { + name string + description string +} + +// NewInt32 creates a new Key for int32 values. +func NewInt32(name, description string) *Int32 { + return &Int32{name: name, description: description} +} + +func (k *Int32) Name() string { return k.name } +func (k *Int32) Description() string { return k.description } + +func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *Int32) Get(lm label.Map) int32 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) } + +// Int64 represents a key +type Int64 struct { + name string + description string +} + +// NewInt64 creates a new Key for int64 values. +func NewInt64(name, description string) *Int64 { + return &Int64{name: name, description: description} +} + +func (k *Int64) Name() string { return k.name } +func (k *Int64) Description() string { return k.description } + +func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendInt(buf, k.From(l), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *Int64) Get(lm label.Map) int64 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) } + +// UInt represents a key +type UInt struct { + name string + description string +} + +// NewUInt creates a new Key for uint values. +func NewUInt(name, description string) *UInt { + return &UInt{name: name, description: description} +} + +func (k *UInt) Name() string { return k.name } +func (k *UInt) Description() string { return k.description } + +func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *UInt) Get(lm label.Map) uint { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) } + +// UInt8 represents a key +type UInt8 struct { + name string + description string +} + +// NewUInt8 creates a new Key for uint8 values. +func NewUInt8(name, description string) *UInt8 { + return &UInt8{name: name, description: description} +} + +func (k *UInt8) Name() string { return k.name } +func (k *UInt8) Description() string { return k.description } + +func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *UInt8) Get(lm label.Map) uint8 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) } + +// UInt16 represents a key +type UInt16 struct { + name string + description string +} + +// NewUInt16 creates a new Key for uint16 values. +func NewUInt16(name, description string) *UInt16 { + return &UInt16{name: name, description: description} +} + +func (k *UInt16) Name() string { return k.name } +func (k *UInt16) Description() string { return k.description } + +func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *UInt16) Get(lm label.Map) uint16 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) } + +// UInt32 represents a key +type UInt32 struct { + name string + description string +} + +// NewUInt32 creates a new Key for uint32 values. +func NewUInt32(name, description string) *UInt32 { + return &UInt32{name: name, description: description} +} + +func (k *UInt32) Name() string { return k.name } +func (k *UInt32) Description() string { return k.description } + +func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) } + +// Get can be used to get a label for the key from a label.Map. +func (k *UInt32) Get(lm label.Map) uint32 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) } + +// UInt64 represents a key +type UInt64 struct { + name string + description string +} + +// NewUInt64 creates a new Key for uint64 values. +func NewUInt64(name, description string) *UInt64 { + return &UInt64{name: name, description: description} +} + +func (k *UInt64) Name() string { return k.name } +func (k *UInt64) Description() string { return k.description } + +func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendUint(buf, k.From(l), 10)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) } + +// Get can be used to get a label for the key from a label.Map. +func (k *UInt64) Get(lm label.Map) uint64 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() } + +// Float32 represents a key +type Float32 struct { + name string + description string +} + +// NewFloat32 creates a new Key for float32 values. +func NewFloat32(name, description string) *Float32 { + return &Float32{name: name, description: description} +} + +func (k *Float32) Name() string { return k.name } +func (k *Float32) Description() string { return k.description } + +func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Float32) Of(v float32) label.Label { + return label.Of64(k, uint64(math.Float32bits(v))) +} + +// Get can be used to get a label for the key from a label.Map. +func (k *Float32) Get(lm label.Map) float32 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *Float32) From(t label.Label) float32 { + return math.Float32frombits(uint32(t.Unpack64())) +} + +// Float64 represents a key +type Float64 struct { + name string + description string +} + +// NewFloat64 creates a new Key for int64 values. +func NewFloat64(name, description string) *Float64 { + return &Float64{name: name, description: description} +} + +func (k *Float64) Name() string { return k.name } +func (k *Float64) Description() string { return k.description } + +func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64)) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Float64) Of(v float64) label.Label { + return label.Of64(k, math.Float64bits(v)) +} + +// Get can be used to get a label for the key from a label.Map. +func (k *Float64) Get(lm label.Map) float64 { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return 0 +} + +// From can be used to get a value from a Label. +func (k *Float64) From(t label.Label) float64 { + return math.Float64frombits(t.Unpack64()) +} + +// String represents a key +type String struct { + name string + description string +} + +// NewString creates a new Key for int64 values. +func NewString(name, description string) *String { + return &String{name: name, description: description} +} + +func (k *String) Name() string { return k.name } +func (k *String) Description() string { return k.description } + +func (k *String) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendQuote(buf, k.From(l))) +} + +// Of creates a new Label with this key and the supplied value. +func (k *String) Of(v string) label.Label { return label.OfString(k, v) } + +// Get can be used to get a label for the key from a label.Map. +func (k *String) Get(lm label.Map) string { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return "" +} + +// From can be used to get a value from a Label. +func (k *String) From(t label.Label) string { return t.UnpackString() } + +// Boolean represents a key +type Boolean struct { + name string + description string +} + +// NewBoolean creates a new Key for bool values. +func NewBoolean(name, description string) *Boolean { + return &Boolean{name: name, description: description} +} + +func (k *Boolean) Name() string { return k.name } +func (k *Boolean) Description() string { return k.description } + +func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) { + w.Write(strconv.AppendBool(buf, k.From(l))) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Boolean) Of(v bool) label.Label { + if v { + return label.Of64(k, 1) + } + return label.Of64(k, 0) +} + +// Get can be used to get a label for the key from a label.Map. +func (k *Boolean) Get(lm label.Map) bool { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return false +} + +// From can be used to get a value from a Label. +func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 } + +// Error represents a key +type Error struct { + name string + description string +} + +// NewError creates a new Key for int64 values. +func NewError(name, description string) *Error { + return &Error{name: name, description: description} +} + +func (k *Error) Name() string { return k.name } +func (k *Error) Description() string { return k.description } + +func (k *Error) Format(w io.Writer, buf []byte, l label.Label) { + io.WriteString(w, k.From(l).Error()) +} + +// Of creates a new Label with this key and the supplied value. +func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) } + +// Get can be used to get a label for the key from a label.Map. +func (k *Error) Get(lm label.Map) error { + if t := lm.Find(k); t.Valid() { + return k.From(t) + } + return nil +} + +// From can be used to get a value from a Label. +func (k *Error) From(t label.Label) error { + err, _ := t.UnpackValue().(error) + return err +} diff --git a/gopls/golang_org_x_tools/event/keys/standard.go b/gopls/golang_org_x_tools/event/keys/standard.go new file mode 100644 index 0000000..7e95866 --- /dev/null +++ b/gopls/golang_org_x_tools/event/keys/standard.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package keys + +var ( + // Msg is a key used to add message strings to label lists. + Msg = NewString("message", "a readable message") + // Label is a key used to indicate an event adds labels to the context. + Label = NewTag("label", "a label context marker") + // Start is used for things like traces that have a name. + Start = NewString("start", "span start") + // Metric is a key used to indicate an event records metrics. + End = NewTag("end", "a span end marker") + // Metric is a key used to indicate an event records metrics. + Detach = NewTag("detach", "a span detach marker") + // Err is a key used to add error values to label lists. + Err = NewError("error", "an error that occurred") + // Metric is a key used to indicate an event records metrics. + Metric = NewTag("metric", "a metric event marker") +) diff --git a/gopls/golang_org_x_tools/event/label/label.go b/gopls/golang_org_x_tools/event/label/label.go new file mode 100644 index 0000000..0f526e1 --- /dev/null +++ b/gopls/golang_org_x_tools/event/label/label.go @@ -0,0 +1,215 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package label + +import ( + "fmt" + "io" + "reflect" + "unsafe" +) + +// Key is used as the identity of a Label. +// Keys are intended to be compared by pointer only, the name should be unique +// for communicating with external systems, but it is not required or enforced. +type Key interface { + // Name returns the key name. + Name() string + // Description returns a string that can be used to describe the value. + Description() string + + // Format is used in formatting to append the value of the label to the + // supplied buffer. + // The formatter may use the supplied buf as a scratch area to avoid + // allocations. + Format(w io.Writer, buf []byte, l Label) +} + +// Label holds a key and value pair. +// It is normally used when passing around lists of labels. +type Label struct { + key Key + packed uint64 + untyped interface{} +} + +// Map is the interface to a collection of Labels indexed by key. +type Map interface { + // Find returns the label that matches the supplied key. + Find(key Key) Label +} + +// List is the interface to something that provides an iterable +// list of labels. +// Iteration should start from 0 and continue until Valid returns false. +type List interface { + // Valid returns true if the index is within range for the list. + // It does not imply the label at that index will itself be valid. + Valid(index int) bool + // Label returns the label at the given index. + Label(index int) Label +} + +// list implements LabelList for a list of Labels. +type list struct { + labels []Label +} + +// filter wraps a LabelList filtering out specific labels. +type filter struct { + keys []Key + underlying List +} + +// listMap implements LabelMap for a simple list of labels. +type listMap struct { + labels []Label +} + +// mapChain implements LabelMap for a list of underlying LabelMap. +type mapChain struct { + maps []Map +} + +// OfValue creates a new label from the key and value. +// This method is for implementing new key types, label creation should +// normally be done with the Of method of the key. +func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} } + +// UnpackValue assumes the label was built using LabelOfValue and returns the value +// that was passed to that constructor. +// This method is for implementing new key types, for type safety normal +// access should be done with the From method of the key. +func (t Label) UnpackValue() interface{} { return t.untyped } + +// Of64 creates a new label from a key and a uint64. This is often +// used for non uint64 values that can be packed into a uint64. +// This method is for implementing new key types, label creation should +// normally be done with the Of method of the key. +func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} } + +// Unpack64 assumes the label was built using LabelOf64 and returns the value that +// was passed to that constructor. +// This method is for implementing new key types, for type safety normal +// access should be done with the From method of the key. +func (t Label) Unpack64() uint64 { return t.packed } + +type stringptr unsafe.Pointer + +// OfString creates a new label from a key and a string. +// This method is for implementing new key types, label creation should +// normally be done with the Of method of the key. +func OfString(k Key, v string) Label { + hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) + return Label{ + key: k, + packed: uint64(hdr.Len), + untyped: stringptr(hdr.Data), + } +} + +// UnpackString assumes the label was built using LabelOfString and returns the +// value that was passed to that constructor. +// This method is for implementing new key types, for type safety normal +// access should be done with the From method of the key. +func (t Label) UnpackString() string { + var v string + hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) + hdr.Data = uintptr(t.untyped.(stringptr)) + hdr.Len = int(t.packed) + return v +} + +// Valid returns true if the Label is a valid one (it has a key). +func (t Label) Valid() bool { return t.key != nil } + +// Key returns the key of this Label. +func (t Label) Key() Key { return t.key } + +// Format is used for debug printing of labels. +func (t Label) Format(f fmt.State, r rune) { + if !t.Valid() { + io.WriteString(f, `nil`) + return + } + io.WriteString(f, t.Key().Name()) + io.WriteString(f, "=") + var buf [128]byte + t.Key().Format(f, buf[:0], t) +} + +func (l *list) Valid(index int) bool { + return index >= 0 && index < len(l.labels) +} + +func (l *list) Label(index int) Label { + return l.labels[index] +} + +func (f *filter) Valid(index int) bool { + return f.underlying.Valid(index) +} + +func (f *filter) Label(index int) Label { + l := f.underlying.Label(index) + for _, f := range f.keys { + if l.Key() == f { + return Label{} + } + } + return l +} + +func (lm listMap) Find(key Key) Label { + for _, l := range lm.labels { + if l.Key() == key { + return l + } + } + return Label{} +} + +func (c mapChain) Find(key Key) Label { + for _, src := range c.maps { + l := src.Find(key) + if l.Valid() { + return l + } + } + return Label{} +} + +var emptyList = &list{} + +func NewList(labels ...Label) List { + if len(labels) == 0 { + return emptyList + } + return &list{labels: labels} +} + +func Filter(l List, keys ...Key) List { + if len(keys) == 0 { + return l + } + return &filter{keys: keys, underlying: l} +} + +func NewMap(labels ...Label) Map { + return listMap{labels: labels} +} + +func MergeMaps(srcs ...Map) Map { + var nonNil []Map + for _, src := range srcs { + if src != nil { + nonNil = append(nonNil, src) + } + } + if len(nonNil) == 1 { + return nonNil[0] + } + return mapChain{maps: nonNil} +} diff --git a/gopls/golang_org_x_tools/event/tag/tag.go b/gopls/golang_org_x_tools/event/tag/tag.go new file mode 100644 index 0000000..33f839e --- /dev/null +++ b/gopls/golang_org_x_tools/event/tag/tag.go @@ -0,0 +1,59 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tag provides the labels used for telemetry throughout gopls. +package tag + +import ( + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/keys" +) + +var ( + // create the label keys we use + Method = keys.NewString("method", "") + StatusCode = keys.NewString("status.code", "") + StatusMessage = keys.NewString("status.message", "") + RPCID = keys.NewString("id", "") + RPCDirection = keys.NewString("direction", "") + File = keys.NewString("file", "") + Directory = keys.New("directory", "") + URI = keys.New("URI", "") + Package = keys.NewString("package", "") // Package ID + PackagePath = keys.NewString("package_path", "") + Query = keys.New("query", "") + Snapshot = keys.NewUInt64("snapshot", "") + Operation = keys.NewString("operation", "") + + Position = keys.New("position", "") + Category = keys.NewString("category", "") + PackageCount = keys.NewInt("packages", "") + Files = keys.New("files", "") + Port = keys.NewInt("port", "") + Type = keys.New("type", "") + HoverKind = keys.NewString("hoverkind", "") + + NewServer = keys.NewString("new_server", "A new server was added") + EndServer = keys.NewString("end_server", "A server was shut down") + + ServerID = keys.NewString("server", "The server ID an event is related to") + Logfile = keys.NewString("logfile", "") + DebugAddress = keys.NewString("debug_address", "") + GoplsPath = keys.NewString("gopls_path", "") + ClientID = keys.NewString("client_id", "") + + Level = keys.NewInt("level", "The logging level") +) + +var ( + // create the stats we measure + Started = keys.NewInt64("started", "Count of started RPCs.") + ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.") //, unit.Bytes) + SentBytes = keys.NewInt64("sent_bytes", "Bytes sent.") //, unit.Bytes) + Latency = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds) +) + +const ( + Inbound = "in" + Outbound = "out" +) diff --git a/gopls/golang_org_x_tools/fakenet/conn.go b/gopls/golang_org_x_tools/fakenet/conn.go new file mode 100644 index 0000000..c9cdaf2 --- /dev/null +++ b/gopls/golang_org_x_tools/fakenet/conn.go @@ -0,0 +1,129 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fakenet + +import ( + "io" + "net" + "sync" + "time" +) + +// NewConn returns a net.Conn built on top of the supplied reader and writer. +// It decouples the read and write on the conn from the underlying stream +// to enable Close to abort ones that are in progress. +// It's primary use is to fake a network connection from stdin and stdout. +func NewConn(name string, in io.ReadCloser, out io.WriteCloser) net.Conn { + c := &fakeConn{ + name: name, + reader: newFeeder(in.Read), + writer: newFeeder(out.Write), + in: in, + out: out, + } + go c.reader.run() + go c.writer.run() + return c +} + +type fakeConn struct { + name string + reader *connFeeder + writer *connFeeder + in io.ReadCloser + out io.WriteCloser +} + +type fakeAddr string + +// connFeeder serializes calls to the source function (io.Reader.Read or +// io.Writer.Write) by delegating them to a channel. This also allows calls to +// be intercepted when the connection is closed, and cancelled early if the +// connection is closed while the calls are still outstanding. +type connFeeder struct { + source func([]byte) (int, error) + input chan []byte + result chan feedResult + mu sync.Mutex + closed bool + done chan struct{} +} + +type feedResult struct { + n int + err error +} + +func (c *fakeConn) Close() error { + c.reader.close() + c.writer.close() + c.in.Close() + c.out.Close() + return nil +} + +func (c *fakeConn) Read(b []byte) (n int, err error) { return c.reader.do(b) } +func (c *fakeConn) Write(b []byte) (n int, err error) { return c.writer.do(b) } +func (c *fakeConn) LocalAddr() net.Addr { return fakeAddr(c.name) } +func (c *fakeConn) RemoteAddr() net.Addr { return fakeAddr(c.name) } +func (c *fakeConn) SetDeadline(t time.Time) error { return nil } +func (c *fakeConn) SetReadDeadline(t time.Time) error { return nil } +func (c *fakeConn) SetWriteDeadline(t time.Time) error { return nil } +func (a fakeAddr) Network() string { return "fake" } +func (a fakeAddr) String() string { return string(a) } + +func newFeeder(source func([]byte) (int, error)) *connFeeder { + return &connFeeder{ + source: source, + input: make(chan []byte), + result: make(chan feedResult), + done: make(chan struct{}), + } +} + +func (f *connFeeder) close() { + f.mu.Lock() + if !f.closed { + f.closed = true + close(f.done) + } + f.mu.Unlock() +} + +func (f *connFeeder) do(b []byte) (n int, err error) { + // send the request to the worker + select { + case f.input <- b: + case <-f.done: + return 0, io.EOF + } + // get the result from the worker + select { + case r := <-f.result: + return r.n, r.err + case <-f.done: + return 0, io.EOF + } +} + +func (f *connFeeder) run() { + var b []byte + for { + // wait for an input request + select { + case b = <-f.input: + case <-f.done: + return + } + // invoke the underlying method + n, err := f.source(b) + // send the result back to the requester + select { + case f.result <- feedResult{n: n, err: err}: + case <-f.done: + return + } + } +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk.go b/gopls/golang_org_x_tools/fastwalk/fastwalk.go new file mode 100644 index 0000000..798fe59 --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk.go @@ -0,0 +1,196 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fastwalk provides a faster version of filepath.Walk for file system +// scanning tools. +package fastwalk + +import ( + "errors" + "os" + "path/filepath" + "runtime" + "sync" +) + +// ErrTraverseLink is used as a return value from WalkFuncs to indicate that the +// symlink named in the call may be traversed. +var ErrTraverseLink = errors.New("fastwalk: traverse symlink, assuming target is a directory") + +// ErrSkipFiles is a used as a return value from WalkFuncs to indicate that the +// callback should not be called for any other files in the current directory. +// Child directories will still be traversed. +var ErrSkipFiles = errors.New("fastwalk: skip remaining files in directory") + +// Walk is a faster implementation of filepath.Walk. +// +// filepath.Walk's design necessarily calls os.Lstat on each file, +// even if the caller needs less info. +// Many tools need only the type of each file. +// On some platforms, this information is provided directly by the readdir +// system call, avoiding the need to stat each file individually. +// fastwalk_unix.go contains a fork of the syscall routines. +// +// See golang.org/issue/16399 +// +// Walk walks the file tree rooted at root, calling walkFn for +// each file or directory in the tree, including root. +// +// If fastWalk returns filepath.SkipDir, the directory is skipped. +// +// Unlike filepath.Walk: +// - file stat calls must be done by the user. +// The only provided metadata is the file type, which does not include +// any permission bits. +// - multiple goroutines stat the filesystem concurrently. The provided +// walkFn must be safe for concurrent use. +// - fastWalk can follow symlinks if walkFn returns the TraverseLink +// sentinel error. It is the walkFn's responsibility to prevent +// fastWalk from going into symlink cycles. +func Walk(root string, walkFn func(path string, typ os.FileMode) error) error { + // TODO(bradfitz): make numWorkers configurable? We used a + // minimum of 4 to give the kernel more info about multiple + // things we want, in hopes its I/O scheduling can take + // advantage of that. Hopefully most are in cache. Maybe 4 is + // even too low of a minimum. Profile more. + numWorkers := 4 + if n := runtime.NumCPU(); n > numWorkers { + numWorkers = n + } + + // Make sure to wait for all workers to finish, otherwise + // walkFn could still be called after returning. This Wait call + // runs after close(e.donec) below. + var wg sync.WaitGroup + defer wg.Wait() + + w := &walker{ + fn: walkFn, + enqueuec: make(chan walkItem, numWorkers), // buffered for performance + workc: make(chan walkItem, numWorkers), // buffered for performance + donec: make(chan struct{}), + + // buffered for correctness & not leaking goroutines: + resc: make(chan error, numWorkers), + } + defer close(w.donec) + + for i := 0; i < numWorkers; i++ { + wg.Add(1) + go w.doWork(&wg) + } + todo := []walkItem{{dir: root}} + out := 0 + for { + workc := w.workc + var workItem walkItem + if len(todo) == 0 { + workc = nil + } else { + workItem = todo[len(todo)-1] + } + select { + case workc <- workItem: + todo = todo[:len(todo)-1] + out++ + case it := <-w.enqueuec: + todo = append(todo, it) + case err := <-w.resc: + out-- + if err != nil { + return err + } + if out == 0 && len(todo) == 0 { + // It's safe to quit here, as long as the buffered + // enqueue channel isn't also readable, which might + // happen if the worker sends both another unit of + // work and its result before the other select was + // scheduled and both w.resc and w.enqueuec were + // readable. + select { + case it := <-w.enqueuec: + todo = append(todo, it) + default: + return nil + } + } + } + } +} + +// doWork reads directories as instructed (via workc) and runs the +// user's callback function. +func (w *walker) doWork(wg *sync.WaitGroup) { + defer wg.Done() + for { + select { + case <-w.donec: + return + case it := <-w.workc: + select { + case <-w.donec: + return + case w.resc <- w.walk(it.dir, !it.callbackDone): + } + } + } +} + +type walker struct { + fn func(path string, typ os.FileMode) error + + donec chan struct{} // closed on fastWalk's return + workc chan walkItem // to workers + enqueuec chan walkItem // from workers + resc chan error // from workers +} + +type walkItem struct { + dir string + callbackDone bool // callback already called; don't do it again +} + +func (w *walker) enqueue(it walkItem) { + select { + case w.enqueuec <- it: + case <-w.donec: + } +} + +func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error { + joined := dirName + string(os.PathSeparator) + baseName + if typ == os.ModeDir { + w.enqueue(walkItem{dir: joined}) + return nil + } + + err := w.fn(joined, typ) + if typ == os.ModeSymlink { + if err == ErrTraverseLink { + // Set callbackDone so we don't call it twice for both the + // symlink-as-symlink and the symlink-as-directory later: + w.enqueue(walkItem{dir: joined, callbackDone: true}) + return nil + } + if err == filepath.SkipDir { + // Permit SkipDir on symlinks too. + return nil + } + } + return err +} + +func (w *walker) walk(root string, runUserCallback bool) error { + if runUserCallback { + err := w.fn(root, os.ModeDir) + if err == filepath.SkipDir { + return nil + } + if err != nil { + return err + } + } + + return readDir(root, w.onDirEnt) +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk_darwin.go b/gopls/golang_org_x_tools/fastwalk/fastwalk_darwin.go new file mode 100644 index 0000000..0ca55e0 --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk_darwin.go @@ -0,0 +1,119 @@ +// 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 darwin && cgo +// +build darwin,cgo + +package fastwalk + +/* +#include + +// fastwalk_readdir_r wraps readdir_r so that we don't have to pass a dirent** +// result pointer which triggers CGO's "Go pointer to Go pointer" check unless +// we allocat the result dirent* with malloc. +// +// fastwalk_readdir_r returns 0 on success, -1 upon reaching the end of the +// directory, or a positive error number to indicate failure. +static int fastwalk_readdir_r(DIR *fd, struct dirent *entry) { + struct dirent *result; + int ret = readdir_r(fd, entry, &result); + if (ret == 0 && result == NULL) { + ret = -1; // EOF + } + return ret; +} +*/ +import "C" + +import ( + "os" + "syscall" + "unsafe" +) + +func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { + fd, err := openDir(dirName) + if err != nil { + return &os.PathError{Op: "opendir", Path: dirName, Err: err} + } + defer C.closedir(fd) + + skipFiles := false + var dirent syscall.Dirent + for { + ret := int(C.fastwalk_readdir_r(fd, (*C.struct_dirent)(unsafe.Pointer(&dirent)))) + if ret != 0 { + if ret == -1 { + break // EOF + } + if ret == int(syscall.EINTR) { + continue + } + return &os.PathError{Op: "readdir", Path: dirName, Err: syscall.Errno(ret)} + } + if dirent.Ino == 0 { + continue + } + typ := dtToType(dirent.Type) + if skipFiles && typ.IsRegular() { + continue + } + name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] + name = name[:dirent.Namlen] + for i, c := range name { + if c == 0 { + name = name[:i] + break + } + } + // Check for useless names before allocating a string. + if string(name) == "." || string(name) == ".." { + continue + } + if err := fn(dirName, string(name), typ); err != nil { + if err != ErrSkipFiles { + return err + } + skipFiles = true + } + } + + return nil +} + +func dtToType(typ uint8) os.FileMode { + switch typ { + case syscall.DT_BLK: + return os.ModeDevice + case syscall.DT_CHR: + return os.ModeDevice | os.ModeCharDevice + case syscall.DT_DIR: + return os.ModeDir + case syscall.DT_FIFO: + return os.ModeNamedPipe + case syscall.DT_LNK: + return os.ModeSymlink + case syscall.DT_REG: + return 0 + case syscall.DT_SOCK: + return os.ModeSocket + } + return ^os.FileMode(0) +} + +// openDir wraps opendir(3) and handles any EINTR errors. The returned *DIR +// needs to be closed with closedir(3). +func openDir(path string) (*C.DIR, error) { + name, err := syscall.BytePtrFromString(path) + if err != nil { + return nil, err + } + for { + fd, err := C.opendir((*C.char)(unsafe.Pointer(name))) + if err != syscall.EINTR { + return fd, err + } + } +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_fileno.go b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_fileno.go new file mode 100644 index 0000000..d58595d --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_fileno.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build freebsd || openbsd || netbsd +// +build freebsd openbsd netbsd + +package fastwalk + +import "syscall" + +func direntInode(dirent *syscall.Dirent) uint64 { + return uint64(dirent.Fileno) +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_ino.go b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_ino.go new file mode 100644 index 0000000..d392289 --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_ino.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (linux || (darwin && !cgo)) && !appengine +// +build linux darwin,!cgo +// +build !appengine + +package fastwalk + +import "syscall" + +func direntInode(dirent *syscall.Dirent) uint64 { + return dirent.Ino +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_namlen_bsd.go b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_namlen_bsd.go new file mode 100644 index 0000000..38a4db6 --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_namlen_bsd.go @@ -0,0 +1,14 @@ +// 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 (darwin && !cgo) || freebsd || openbsd || netbsd +// +build darwin,!cgo freebsd openbsd netbsd + +package fastwalk + +import "syscall" + +func direntNamlen(dirent *syscall.Dirent) uint64 { + return uint64(dirent.Namlen) +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_namlen_linux.go b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_namlen_linux.go new file mode 100644 index 0000000..c82e57d --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk_dirent_namlen_linux.go @@ -0,0 +1,29 @@ +// 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 linux && !appengine +// +build linux,!appengine + +package fastwalk + +import ( + "bytes" + "syscall" + "unsafe" +) + +func direntNamlen(dirent *syscall.Dirent) uint64 { + const fixedHdr = uint16(unsafe.Offsetof(syscall.Dirent{}.Name)) + nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) + const nameBufLen = uint16(len(nameBuf)) + limit := dirent.Reclen - fixedHdr + if limit > nameBufLen { + limit = nameBufLen + } + nameLen := bytes.IndexByte(nameBuf[:limit], 0) + if nameLen < 0 { + panic("failed to find terminating 0 byte in dirent") + } + return uint64(nameLen) +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk_portable.go b/gopls/golang_org_x_tools/fastwalk/fastwalk_portable.go new file mode 100644 index 0000000..085d311 --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk_portable.go @@ -0,0 +1,38 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build appengine || (!linux && !darwin && !freebsd && !openbsd && !netbsd) +// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd + +package fastwalk + +import ( + "io/ioutil" + "os" +) + +// readDir calls fn for each directory entry in dirName. +// It does not descend into directories or follow symlinks. +// If fn returns a non-nil error, readDir returns with that error +// immediately. +func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { + fis, err := ioutil.ReadDir(dirName) + if err != nil { + return err + } + skipFiles := false + for _, fi := range fis { + if fi.Mode().IsRegular() && skipFiles { + continue + } + if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil { + if err == ErrSkipFiles { + skipFiles = true + continue + } + return err + } + } + return nil +} diff --git a/gopls/golang_org_x_tools/fastwalk/fastwalk_unix.go b/gopls/golang_org_x_tools/fastwalk/fastwalk_unix.go new file mode 100644 index 0000000..f12f1a7 --- /dev/null +++ b/gopls/golang_org_x_tools/fastwalk/fastwalk_unix.go @@ -0,0 +1,153 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (linux || freebsd || openbsd || netbsd || (darwin && !cgo)) && !appengine +// +build linux freebsd openbsd netbsd darwin,!cgo +// +build !appengine + +package fastwalk + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +const blockSize = 8 << 10 + +// unknownFileMode is a sentinel (and bogus) os.FileMode +// value used to represent a syscall.DT_UNKNOWN Dirent.Type. +const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice + +func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { + fd, err := open(dirName, 0, 0) + if err != nil { + return &os.PathError{Op: "open", Path: dirName, Err: err} + } + defer syscall.Close(fd) + + // The buffer must be at least a block long. + buf := make([]byte, blockSize) // stack-allocated; doesn't escape + bufp := 0 // starting read position in buf + nbuf := 0 // end valid data in buf + skipFiles := false + for { + if bufp >= nbuf { + bufp = 0 + nbuf, err = readDirent(fd, buf) + if err != nil { + return os.NewSyscallError("readdirent", err) + } + if nbuf <= 0 { + return nil + } + } + consumed, name, typ := parseDirEnt(buf[bufp:nbuf]) + bufp += consumed + if name == "" || name == "." || name == ".." { + continue + } + // Fallback for filesystems (like old XFS) that don't + // support Dirent.Type and have DT_UNKNOWN (0) there + // instead. + if typ == unknownFileMode { + fi, err := os.Lstat(dirName + "/" + name) + if err != nil { + // It got deleted in the meantime. + if os.IsNotExist(err) { + continue + } + return err + } + typ = fi.Mode() & os.ModeType + } + if skipFiles && typ.IsRegular() { + continue + } + if err := fn(dirName, name, typ); err != nil { + if err == ErrSkipFiles { + skipFiles = true + continue + } + return err + } + } +} + +func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { + // golang.org/issue/37269 + dirent := &syscall.Dirent{} + copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf) + if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { + panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v)) + } + if len(buf) < int(dirent.Reclen) { + panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen)) + } + consumed = int(dirent.Reclen) + if direntInode(dirent) == 0 { // File absent in directory. + return + } + switch dirent.Type { + case syscall.DT_REG: + typ = 0 + case syscall.DT_DIR: + typ = os.ModeDir + case syscall.DT_LNK: + typ = os.ModeSymlink + case syscall.DT_BLK: + typ = os.ModeDevice + case syscall.DT_FIFO: + typ = os.ModeNamedPipe + case syscall.DT_SOCK: + typ = os.ModeSocket + case syscall.DT_UNKNOWN: + typ = unknownFileMode + default: + // Skip weird things. + // It's probably a DT_WHT (http://lwn.net/Articles/325369/) + // or something. Revisit if/when this package is moved outside + // of goimports. goimports only cares about regular files, + // symlinks, and directories. + return + } + + nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) + nameLen := direntNamlen(dirent) + + // Special cases for common things: + if nameLen == 1 && nameBuf[0] == '.' { + name = "." + } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' { + name = ".." + } else { + name = string(nameBuf[:nameLen]) + } + return +} + +// According to https://golang.org/doc/go1.14#runtime +// A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS +// systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases. +// +// This causes syscall.Open and syscall.ReadDirent sometimes fail with EINTR errors. +// We need to retry in this case. +func open(path string, mode int, perm uint32) (fd int, err error) { + for { + fd, err := syscall.Open(path, mode, perm) + if err != syscall.EINTR { + return fd, err + } + } +} + +func readDirent(fd int, buf []byte) (n int, err error) { + for { + nbuf, err := syscall.ReadDirent(fd, buf) + if err != syscall.EINTR { + return nbuf, err + } + } +} diff --git a/gopls/golang_org_x_tools/fuzzy/input.go b/gopls/golang_org_x_tools/fuzzy/input.go new file mode 100644 index 0000000..c103816 --- /dev/null +++ b/gopls/golang_org_x_tools/fuzzy/input.go @@ -0,0 +1,183 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzzy + +import ( + "unicode" +) + +// RuneRole specifies the role of a rune in the context of an input. +type RuneRole byte + +const ( + // RNone specifies a rune without any role in the input (i.e., whitespace/non-ASCII). + RNone RuneRole = iota + // RSep specifies a rune with the role of segment separator. + RSep + // RTail specifies a rune which is a lower-case tail in a word in the input. + RTail + // RUCTail specifies a rune which is an upper-case tail in a word in the input. + RUCTail + // RHead specifies a rune which is the first character in a word in the input. + RHead +) + +// RuneRoles detects the roles of each byte rune in an input string and stores it in the output +// slice. The rune role depends on the input type. Stops when it parsed all the runes in the string +// or when it filled the output. If output is nil, then it gets created. +func RuneRoles(candidate []byte, reuse []RuneRole) []RuneRole { + var output []RuneRole + if cap(reuse) < len(candidate) { + output = make([]RuneRole, 0, len(candidate)) + } else { + output = reuse[:0] + } + + prev, prev2 := rtNone, rtNone + for i := 0; i < len(candidate); i++ { + r := rune(candidate[i]) + + role := RNone + + curr := rtLower + if candidate[i] <= unicode.MaxASCII { + curr = runeType(rt[candidate[i]] - '0') + } + + if curr == rtLower { + if prev == rtNone || prev == rtPunct { + role = RHead + } else { + role = RTail + } + } else if curr == rtUpper { + role = RHead + + if prev == rtUpper { + // This and previous characters are both upper case. + + if i+1 == len(candidate) { + // This is last character, previous was also uppercase -> this is UCTail + // i.e., (current char is C): aBC / BC / ABC + role = RUCTail + } + } + } else if curr == rtPunct { + switch r { + case '.', ':': + role = RSep + } + } + if curr != rtLower { + if i > 1 && output[i-1] == RHead && prev2 == rtUpper && (output[i-2] == RHead || output[i-2] == RUCTail) { + // The previous two characters were uppercase. The current one is not a lower case, so the + // previous one can't be a HEAD. Make it a UCTail. + // i.e., (last char is current char - B must be a UCTail): ABC / ZABC / AB. + output[i-1] = RUCTail + } + } + + output = append(output, role) + prev2 = prev + prev = curr + } + return output +} + +type runeType byte + +const ( + rtNone runeType = iota + rtPunct + rtLower + rtUpper +) + +const rt = "00000000000000000000000000000000000000000000001122222222221000000333333333333333333333333330000002222222222222222222222222200000" + +// LastSegment returns the substring representing the last segment from the input, where each +// byte has an associated RuneRole in the roles slice. This makes sense only for inputs of Symbol +// or Filename type. +func LastSegment(input string, roles []RuneRole) string { + // Exclude ending separators. + end := len(input) - 1 + for end >= 0 && roles[end] == RSep { + end-- + } + if end < 0 { + return "" + } + + start := end - 1 + for start >= 0 && roles[start] != RSep { + start-- + } + + return input[start+1 : end+1] +} + +// fromChunks copies string chunks into the given buffer. +func fromChunks(chunks []string, buffer []byte) []byte { + ii := 0 + for _, chunk := range chunks { + for i := 0; i < len(chunk); i++ { + if ii >= cap(buffer) { + break + } + buffer[ii] = chunk[i] + ii++ + } + } + return buffer[:ii] +} + +// toLower transforms the input string to lower case, which is stored in the output byte slice. +// The lower casing considers only ASCII values - non ASCII values are left unmodified. +// Stops when parsed all input or when it filled the output slice. If output is nil, then it gets +// created. +func toLower(input []byte, reuse []byte) []byte { + output := reuse + if cap(reuse) < len(input) { + output = make([]byte, len(input)) + } + + for i := 0; i < len(input); i++ { + r := rune(input[i]) + if input[i] <= unicode.MaxASCII { + if 'A' <= r && r <= 'Z' { + r += 'a' - 'A' + } + } + output[i] = byte(r) + } + return output[:len(input)] +} + +// WordConsumer defines a consumer for a word delimited by the [start,end) byte offsets in an input +// (start is inclusive, end is exclusive). +type WordConsumer func(start, end int) + +// Words find word delimiters in an input based on its bytes' mappings to rune roles. The offset +// delimiters for each word are fed to the provided consumer function. +func Words(roles []RuneRole, consume WordConsumer) { + var wordStart int + for i, r := range roles { + switch r { + case RUCTail, RTail: + case RHead, RNone, RSep: + if i != wordStart { + consume(wordStart, i) + } + wordStart = i + if r != RHead { + // Skip this character. + wordStart = i + 1 + } + } + } + if wordStart != len(roles) { + consume(wordStart, len(roles)) + } +} diff --git a/gopls/golang_org_x_tools/fuzzy/matcher.go b/gopls/golang_org_x_tools/fuzzy/matcher.go new file mode 100644 index 0000000..c0efd30 --- /dev/null +++ b/gopls/golang_org_x_tools/fuzzy/matcher.go @@ -0,0 +1,434 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fuzzy implements a fuzzy matching algorithm. +package fuzzy + +import ( + "bytes" + "fmt" +) + +const ( + // MaxInputSize is the maximum size of the input scored against the fuzzy matcher. Longer inputs + // will be truncated to this size. + MaxInputSize = 127 + // MaxPatternSize is the maximum size of the pattern used to construct the fuzzy matcher. Longer + // inputs are truncated to this size. + MaxPatternSize = 63 +) + +type scoreVal int + +func (s scoreVal) val() int { + return int(s) >> 1 +} + +func (s scoreVal) prevK() int { + return int(s) & 1 +} + +func score(val int, prevK int /*0 or 1*/) scoreVal { + return scoreVal(val<<1 + prevK) +} + +// Matcher implements a fuzzy matching algorithm for scoring candidates against a pattern. +// The matcher does not support parallel usage. +type Matcher struct { + pattern string + patternLower []byte // lower-case version of the pattern + patternShort []byte // first characters of the pattern + caseSensitive bool // set if the pattern is mix-cased + + patternRoles []RuneRole // the role of each character in the pattern + roles []RuneRole // the role of each character in the tested string + + scores [MaxInputSize + 1][MaxPatternSize + 1][2]scoreVal + + scoreScale float32 + + lastCandidateLen int // in bytes + lastCandidateMatched bool + + // Reusable buffers to avoid allocating for every candidate. + // - inputBuf stores the concatenated input chunks + // - lowerBuf stores the last candidate in lower-case + // - rolesBuf stores the calculated roles for each rune in the last + // candidate. + inputBuf [MaxInputSize]byte + lowerBuf [MaxInputSize]byte + rolesBuf [MaxInputSize]RuneRole +} + +func (m *Matcher) bestK(i, j int) int { + if m.scores[i][j][0].val() < m.scores[i][j][1].val() { + return 1 + } + return 0 +} + +// NewMatcher returns a new fuzzy matcher for scoring candidates against the provided pattern. +func NewMatcher(pattern string) *Matcher { + if len(pattern) > MaxPatternSize { + pattern = pattern[:MaxPatternSize] + } + + m := &Matcher{ + pattern: pattern, + patternLower: toLower([]byte(pattern), nil), + } + + for i, c := range m.patternLower { + if pattern[i] != c { + m.caseSensitive = true + break + } + } + + if len(pattern) > 3 { + m.patternShort = m.patternLower[:3] + } else { + m.patternShort = m.patternLower + } + + m.patternRoles = RuneRoles([]byte(pattern), nil) + + if len(pattern) > 0 { + maxCharScore := 4 + m.scoreScale = 1 / float32(maxCharScore*len(pattern)) + } + + return m +} + +// Score returns the score returned by matching the candidate to the pattern. +// This is not designed for parallel use. Multiple candidates must be scored sequentially. +// Returns a score between 0 and 1 (0 - no match, 1 - perfect match). +func (m *Matcher) Score(candidate string) float32 { + return m.ScoreChunks([]string{candidate}) +} + +func (m *Matcher) ScoreChunks(chunks []string) float32 { + candidate := fromChunks(chunks, m.inputBuf[:]) + if len(candidate) > MaxInputSize { + candidate = candidate[:MaxInputSize] + } + lower := toLower(candidate, m.lowerBuf[:]) + m.lastCandidateLen = len(candidate) + + if len(m.pattern) == 0 { + // Empty patterns perfectly match candidates. + return 1 + } + + if m.match(candidate, lower) { + sc := m.computeScore(candidate, lower) + if sc > minScore/2 && !m.poorMatch() { + m.lastCandidateMatched = true + if len(m.pattern) == len(candidate) { + // Perfect match. + return 1 + } + + if sc < 0 { + sc = 0 + } + normalizedScore := float32(sc) * m.scoreScale + if normalizedScore > 1 { + normalizedScore = 1 + } + + return normalizedScore + } + } + + m.lastCandidateMatched = false + return 0 +} + +const minScore = -10000 + +// MatchedRanges returns matches ranges for the last scored string as a flattened array of +// [begin, end) byte offset pairs. +func (m *Matcher) MatchedRanges() []int { + if len(m.pattern) == 0 || !m.lastCandidateMatched { + return nil + } + i, j := m.lastCandidateLen, len(m.pattern) + if m.scores[i][j][0].val() < minScore/2 && m.scores[i][j][1].val() < minScore/2 { + return nil + } + + var ret []int + k := m.bestK(i, j) + for i > 0 { + take := (k == 1) + k = m.scores[i][j][k].prevK() + if take { + if len(ret) == 0 || ret[len(ret)-1] != i { + ret = append(ret, i) + ret = append(ret, i-1) + } else { + ret[len(ret)-1] = i - 1 + } + j-- + } + i-- + } + // Reverse slice. + for i := 0; i < len(ret)/2; i++ { + ret[i], ret[len(ret)-1-i] = ret[len(ret)-1-i], ret[i] + } + return ret +} + +func (m *Matcher) match(candidate []byte, candidateLower []byte) bool { + i, j := 0, 0 + for ; i < len(candidateLower) && j < len(m.patternLower); i++ { + if candidateLower[i] == m.patternLower[j] { + j++ + } + } + if j != len(m.patternLower) { + return false + } + + // The input passes the simple test against pattern, so it is time to classify its characters. + // Character roles are used below to find the last segment. + m.roles = RuneRoles(candidate, m.rolesBuf[:]) + + return true +} + +func (m *Matcher) computeScore(candidate []byte, candidateLower []byte) int { + pattLen, candLen := len(m.pattern), len(candidate) + + for j := 0; j <= len(m.pattern); j++ { + m.scores[0][j][0] = minScore << 1 + m.scores[0][j][1] = minScore << 1 + } + m.scores[0][0][0] = score(0, 0) // Start with 0. + + segmentsLeft, lastSegStart := 1, 0 + for i := 0; i < candLen; i++ { + if m.roles[i] == RSep { + segmentsLeft++ + lastSegStart = i + 1 + } + } + + // A per-character bonus for a consecutive match. + consecutiveBonus := 2 + wordIdx := 0 // Word count within segment. + for i := 1; i <= candLen; i++ { + + role := m.roles[i-1] + isHead := role == RHead + + if isHead { + wordIdx++ + } else if role == RSep && segmentsLeft > 1 { + wordIdx = 0 + segmentsLeft-- + } + + var skipPenalty int + if i == 1 || (i-1) == lastSegStart { + // Skipping the start of first or last segment. + skipPenalty++ + } + + for j := 0; j <= pattLen; j++ { + // By default, we don't have a match. Fill in the skip data. + m.scores[i][j][1] = minScore << 1 + + // Compute the skip score. + k := 0 + if m.scores[i-1][j][0].val() < m.scores[i-1][j][1].val() { + k = 1 + } + + skipScore := m.scores[i-1][j][k].val() + // Do not penalize missing characters after the last matched segment. + if j != pattLen { + skipScore -= skipPenalty + } + m.scores[i][j][0] = score(skipScore, k) + + if j == 0 || candidateLower[i-1] != m.patternLower[j-1] { + // Not a match. + continue + } + pRole := m.patternRoles[j-1] + + if role == RTail && pRole == RHead { + if j > 1 { + // Not a match: a head in the pattern matches a tail character in the candidate. + continue + } + // Special treatment for the first character of the pattern. We allow + // matches in the middle of a word if they are long enough, at least + // min(3, pattern.length) characters. + if !bytes.HasPrefix(candidateLower[i-1:], m.patternShort) { + continue + } + } + + // Compute the char score. + var charScore int + // Bonus 1: the char is in the candidate's last segment. + if segmentsLeft <= 1 { + charScore++ + } + // Bonus 2: Case match or a Head in the pattern aligns with one in the word. + // Single-case patterns lack segmentation signals and we assume any character + // can be a head of a segment. + if candidate[i-1] == m.pattern[j-1] || role == RHead && (!m.caseSensitive || pRole == RHead) { + charScore++ + } + + // Penalty 1: pattern char is Head, candidate char is Tail. + if role == RTail && pRole == RHead { + charScore-- + } + // Penalty 2: first pattern character matched in the middle of a word. + if j == 1 && role == RTail { + charScore -= 4 + } + + // Third dimension encodes whether there is a gap between the previous match and the current + // one. + for k := 0; k < 2; k++ { + sc := m.scores[i-1][j-1][k].val() + charScore + + isConsecutive := k == 1 || i-1 == 0 || i-1 == lastSegStart + if isConsecutive { + // Bonus 3: a consecutive match. First character match also gets a bonus to + // ensure prefix final match score normalizes to 1.0. + // Logically, this is a part of charScore, but we have to compute it here because it + // only applies for consecutive matches (k == 1). + sc += consecutiveBonus + } + if k == 0 { + // Penalty 3: Matching inside a segment (and previous char wasn't matched). Penalize for the lack + // of alignment. + if role == RTail || role == RUCTail { + sc -= 3 + } + } + + if sc > m.scores[i][j][1].val() { + m.scores[i][j][1] = score(sc, k) + } + } + } + } + + result := m.scores[len(candidate)][len(m.pattern)][m.bestK(len(candidate), len(m.pattern))].val() + + return result +} + +// ScoreTable returns the score table computed for the provided candidate. Used only for debugging. +func (m *Matcher) ScoreTable(candidate string) string { + var buf bytes.Buffer + + var line1, line2, separator bytes.Buffer + line1.WriteString("\t") + line2.WriteString("\t") + for j := 0; j < len(m.pattern); j++ { + line1.WriteString(fmt.Sprintf("%c\t\t", m.pattern[j])) + separator.WriteString("----------------") + } + + buf.WriteString(line1.String()) + buf.WriteString("\n") + buf.WriteString(separator.String()) + buf.WriteString("\n") + + for i := 1; i <= len(candidate); i++ { + line1.Reset() + line2.Reset() + + line1.WriteString(fmt.Sprintf("%c\t", candidate[i-1])) + line2.WriteString("\t") + + for j := 1; j <= len(m.pattern); j++ { + line1.WriteString(fmt.Sprintf("M%6d(%c)\t", m.scores[i][j][0].val(), dir(m.scores[i][j][0].prevK()))) + line2.WriteString(fmt.Sprintf("H%6d(%c)\t", m.scores[i][j][1].val(), dir(m.scores[i][j][1].prevK()))) + } + buf.WriteString(line1.String()) + buf.WriteString("\n") + buf.WriteString(line2.String()) + buf.WriteString("\n") + buf.WriteString(separator.String()) + buf.WriteString("\n") + } + + return buf.String() +} + +func dir(prevK int) rune { + if prevK == 0 { + return 'M' + } + return 'H' +} + +func (m *Matcher) poorMatch() bool { + if len(m.pattern) < 2 { + return false + } + + i, j := m.lastCandidateLen, len(m.pattern) + k := m.bestK(i, j) + + var counter, len int + for i > 0 { + take := (k == 1) + k = m.scores[i][j][k].prevK() + if take { + len++ + if k == 0 && len < 3 && m.roles[i-1] == RTail { + // Short match in the middle of a word + counter++ + if counter > 1 { + return true + } + } + j-- + } else { + len = 0 + } + i-- + } + return false +} + +// BestMatch returns the name most similar to the +// pattern, using fuzzy matching, or the empty string. +func BestMatch(pattern string, names []string) string { + fuzz := NewMatcher(pattern) + best := "" + highScore := float32(0) // minimum score is 0 (no match) + for _, name := range names { + // TODO: Improve scoring algorithm. + score := fuzz.Score(name) + if score > highScore { + highScore = score + best = name + } else if score == 0 { + // Order matters in the fuzzy matching algorithm. If we find no match + // when matching the target to the identifier, try matching the identifier + // to the target. + revFuzz := NewMatcher(name) + revScore := revFuzz.Score(pattern) + if revScore > highScore { + highScore = revScore + best = name + } + } + } + return best +} diff --git a/gopls/golang_org_x_tools/fuzzy/symbol.go b/gopls/golang_org_x_tools/fuzzy/symbol.go new file mode 100644 index 0000000..073a4cd --- /dev/null +++ b/gopls/golang_org_x_tools/fuzzy/symbol.go @@ -0,0 +1,237 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzzy + +import ( + "unicode" +) + +// SymbolMatcher implements a fuzzy matching algorithm optimized for Go symbols +// of the form: +// +// example.com/path/to/package.object.field +// +// Knowing that we are matching symbols like this allows us to make the +// following optimizations: +// - We can incorporate right-to-left relevance directly into the score +// calculation. +// - We can match from right to left, discarding leading bytes if the input is +// too long. +// - We just take the right-most match without losing too much precision. This +// allows us to use an O(n) algorithm. +// - We can operate directly on chunked strings; in many cases we will +// be storing the package path and/or package name separately from the +// symbol or identifiers, so doing this avoids allocating strings. +// - We can return the index of the right-most match, allowing us to trim +// irrelevant qualification. +// +// This implementation is experimental, serving as a reference fast algorithm +// to compare to the fuzzy algorithm implemented by Matcher. +type SymbolMatcher struct { + // Using buffers of length 256 is both a reasonable size for most qualified + // symbols, and makes it easy to avoid bounds checks by using uint8 indexes. + pattern [256]rune + patternLen uint8 + inputBuffer [256]rune // avoid allocating when considering chunks + roles [256]uint32 // which roles does a rune play (word start, etc.) + segments [256]uint8 // how many segments from the right is each rune +} + +const ( + segmentStart uint32 = 1 << iota + wordStart + separator +) + +// NewSymbolMatcher creates a SymbolMatcher that may be used to match the given +// search pattern. +// +// Currently this matcher only accepts case-insensitive fuzzy patterns. +// +// An empty pattern matches no input. +func NewSymbolMatcher(pattern string) *SymbolMatcher { + m := &SymbolMatcher{} + for _, p := range pattern { + m.pattern[m.patternLen] = unicode.ToLower(p) + m.patternLen++ + if m.patternLen == 255 || int(m.patternLen) == len(pattern) { + // break at 255 so that we can represent patternLen with a uint8. + break + } + } + return m +} + +// Match looks for the right-most match of the search pattern within the symbol +// represented by concatenating the given chunks, returning its offset and +// score. +// +// If a match is found, the first return value will hold the absolute byte +// offset within all chunks for the start of the symbol. In other words, the +// index of the match within strings.Join(chunks, ""). If no match is found, +// the first return value will be -1. +// +// The second return value will be the score of the match, which is always +// between 0 and 1, inclusive. A score of 0 indicates no match. +func (m *SymbolMatcher) Match(chunks []string) (int, float64) { + // Explicit behavior for an empty pattern. + // + // As a minor optimization, this also avoids nilness checks later on, since + // the compiler can prove that m != nil. + if m.patternLen == 0 { + return -1, 0 + } + + // First phase: populate the input buffer with lower-cased runes. + // + // We could also check for a forward match here, but since we'd have to write + // the entire input anyway this has negligible impact on performance. + + var ( + inputLen = uint8(0) + modifiers = wordStart | segmentStart + ) + +input: + for _, chunk := range chunks { + for _, r := range chunk { + if r == '.' || r == '/' { + modifiers |= separator + } + // optimization: avoid calls to unicode.ToLower, which can't be inlined. + l := r + if r <= unicode.MaxASCII { + if 'A' <= r && r <= 'Z' { + l = r + 'a' - 'A' + } + } else { + l = unicode.ToLower(r) + } + if l != r { + modifiers |= wordStart + } + m.inputBuffer[inputLen] = l + m.roles[inputLen] = modifiers + inputLen++ + if m.roles[inputLen-1]&separator != 0 { + modifiers = wordStart | segmentStart + } else { + modifiers = 0 + } + // TODO: we should prefer the right-most input if it overflows, rather + // than the left-most as we're doing here. + if inputLen == 255 { + break input + } + } + } + + // Second phase: find the right-most match, and count segments from the + // right. + + var ( + pi = uint8(m.patternLen - 1) // pattern index + p = m.pattern[pi] // pattern rune + start = -1 // start offset of match + rseg = uint8(0) + ) + const maxSeg = 3 // maximum number of segments from the right to count, for scoring purposes. + + for ii := inputLen - 1; ; ii-- { + r := m.inputBuffer[ii] + if rseg < maxSeg && m.roles[ii]&separator != 0 { + rseg++ + } + m.segments[ii] = rseg + if p == r { + if pi == 0 { + start = int(ii) + break + } + pi-- + p = m.pattern[pi] + } + // Don't check ii >= 0 in the loop condition: ii is a uint8. + if ii == 0 { + break + } + } + + if start < 0 { + // no match: skip scoring + return -1, 0 + } + + // Third phase: find the shortest match, and compute the score. + + // Score is the average score for each character. + // + // A character score is the multiple of: + // 1. 1.0 if the character starts a segment, .8 if the character start a + // mid-segment word, otherwise 0.6. This carries over to immediately + // following characters. + // 2. For the final character match, the multiplier from (1) is reduced to + // .8 if the next character in the input is a mid-segment word, or 0.6 if + // the next character in the input is not a word or segment start. This + // ensures that we favor whole-word or whole-segment matches over prefix + // matches. + // 3. 1.0 if the character is part of the last segment, otherwise + // 1.0-.2*, with a max segment count of 3. + // + // This is a very naive algorithm, but it is fast. There's lots of prior art + // here, and we should leverage it. For example, we could explicitly consider + // character distance, and exact matches of words or segments. + // + // Also note that this might not actually find the highest scoring match, as + // doing so could require a non-linear algorithm, depending on how the score + // is calculated. + + pi = 0 + p = m.pattern[pi] + + const ( + segStreak = 1.0 + wordStreak = 0.8 + noStreak = 0.6 + perSegment = 0.2 // we count at most 3 segments above + ) + + streakBonus := noStreak + totScore := 0.0 + for ii := uint8(start); ii < inputLen; ii++ { + r := m.inputBuffer[ii] + if r == p { + pi++ + p = m.pattern[pi] + // Note: this could be optimized with some bit operations. + switch { + case m.roles[ii]&segmentStart != 0 && segStreak > streakBonus: + streakBonus = segStreak + case m.roles[ii]&wordStart != 0 && wordStreak > streakBonus: + streakBonus = wordStreak + } + finalChar := pi >= m.patternLen + // finalCost := 1.0 + if finalChar && streakBonus > noStreak { + switch { + case ii == inputLen-1 || m.roles[ii+1]&segmentStart != 0: + // Full segment: no reduction + case m.roles[ii+1]&wordStart != 0: + streakBonus = wordStreak + default: + streakBonus = noStreak + } + } + totScore += streakBonus * (1.0 - float64(m.segments[ii])*perSegment) + if finalChar { + break + } + } else { + streakBonus = noStreak + } + } + + return start, totScore / float64(m.patternLen) +} diff --git a/gopls/golang_org_x_tools/gcimporter/bexport.go b/gopls/golang_org_x_tools/gcimporter/bexport.go new file mode 100644 index 0000000..30582ed --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/bexport.go @@ -0,0 +1,852 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Binary package export. +// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go; +// see that file for specification of the format. + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "go/types" + "math" + "math/big" + "sort" + "strings" +) + +// If debugFormat is set, each integer and string value is preceded by a marker +// and position information in the encoding. This mechanism permits an importer +// to recognize immediately when it is out of sync. The importer recognizes this +// mode automatically (i.e., it can import export data produced with debugging +// support even if debugFormat is not set at the time of import). This mode will +// lead to massively larger export data (by a factor of 2 to 3) and should only +// be enabled during development and debugging. +// +// NOTE: This flag is the first flag to enable if importing dies because of +// (suspected) format errors, and whenever a change is made to the format. +const debugFormat = false // default: false + +// Current export format version. Increase with each format change. +// +// Note: The latest binary (non-indexed) export format is at version 6. +// This exporter is still at level 4, but it doesn't matter since +// the binary importer can handle older versions just fine. +// +// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE +// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE +// 4: type name objects support type aliases, uses aliasTag +// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) +// 2: removed unused bool in ODCL export (compiler only) +// 1: header format change (more regular), export package for _ struct fields +// 0: Go1.7 encoding +const exportVersion = 4 + +// trackAllTypes enables cycle tracking for all types, not just named +// types. The existing compiler invariants assume that unnamed types +// that are not completely set up are not used, or else there are spurious +// errors. +// If disabled, only named types are tracked, possibly leading to slightly +// less efficient encoding in rare cases. It also prevents the export of +// some corner-case type declarations (but those are not handled correctly +// with with the textual export format either). +// TODO(gri) enable and remove once issues caused by it are fixed +const trackAllTypes = false + +type exporter struct { + fset *token.FileSet + out bytes.Buffer + + // object -> index maps, indexed in order of serialization + strIndex map[string]int + pkgIndex map[*types.Package]int + typIndex map[types.Type]int + + // position encoding + posInfoFormat bool + prevFile string + prevLine int + + // debugging support + written int // bytes written + indent int // for trace +} + +// internalError represents an error generated inside this package. +type internalError string + +func (e internalError) Error() string { return "gcimporter: " + string(e) } + +func internalErrorf(format string, args ...interface{}) error { + return internalError(fmt.Sprintf(format, args...)) +} + +// BExportData returns binary export data for pkg. +// If no file set is provided, position info will be missing. +func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { + if !debug { + defer func() { + if e := recover(); e != nil { + if ierr, ok := e.(internalError); ok { + err = ierr + return + } + // Not an internal error; panic again. + panic(e) + } + }() + } + + p := exporter{ + fset: fset, + strIndex: map[string]int{"": 0}, // empty string is mapped to 0 + pkgIndex: make(map[*types.Package]int), + typIndex: make(map[types.Type]int), + posInfoFormat: true, // TODO(gri) might become a flag, eventually + } + + // write version info + // The version string must start with "version %d" where %d is the version + // number. Additional debugging information may follow after a blank; that + // text is ignored by the importer. + p.rawStringln(fmt.Sprintf("version %d", exportVersion)) + var debug string + if debugFormat { + debug = "debug" + } + p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly + p.bool(trackAllTypes) + p.bool(p.posInfoFormat) + + // --- generic export data --- + + // populate type map with predeclared "known" types + for index, typ := range predeclared() { + p.typIndex[typ] = index + } + if len(p.typIndex) != len(predeclared()) { + return nil, internalError("duplicate entries in type map?") + } + + // write package data + p.pkg(pkg, true) + if trace { + p.tracef("\n") + } + + // write objects + objcount := 0 + scope := pkg.Scope() + for _, name := range scope.Names() { + if !token.IsExported(name) { + continue + } + if trace { + p.tracef("\n") + } + p.obj(scope.Lookup(name)) + objcount++ + } + + // indicate end of list + if trace { + p.tracef("\n") + } + p.tag(endTag) + + // for self-verification only (redundant) + p.int(objcount) + + if trace { + p.tracef("\n") + } + + // --- end of export data --- + + return p.out.Bytes(), nil +} + +func (p *exporter) pkg(pkg *types.Package, emptypath bool) { + if pkg == nil { + panic(internalError("unexpected nil pkg")) + } + + // if we saw the package before, write its index (>= 0) + if i, ok := p.pkgIndex[pkg]; ok { + p.index('P', i) + return + } + + // otherwise, remember the package, write the package tag (< 0) and package data + if trace { + p.tracef("P%d = { ", len(p.pkgIndex)) + defer p.tracef("} ") + } + p.pkgIndex[pkg] = len(p.pkgIndex) + + p.tag(packageTag) + p.string(pkg.Name()) + if emptypath { + p.string("") + } else { + p.string(pkg.Path()) + } +} + +func (p *exporter) obj(obj types.Object) { + switch obj := obj.(type) { + case *types.Const: + p.tag(constTag) + p.pos(obj) + p.qualifiedName(obj) + p.typ(obj.Type()) + p.value(obj.Val()) + + case *types.TypeName: + if obj.IsAlias() { + p.tag(aliasTag) + p.pos(obj) + p.qualifiedName(obj) + } else { + p.tag(typeTag) + } + p.typ(obj.Type()) + + case *types.Var: + p.tag(varTag) + p.pos(obj) + p.qualifiedName(obj) + p.typ(obj.Type()) + + case *types.Func: + p.tag(funcTag) + p.pos(obj) + p.qualifiedName(obj) + sig := obj.Type().(*types.Signature) + p.paramList(sig.Params(), sig.Variadic()) + p.paramList(sig.Results(), false) + + default: + panic(internalErrorf("unexpected object %v (%T)", obj, obj)) + } +} + +func (p *exporter) pos(obj types.Object) { + if !p.posInfoFormat { + return + } + + file, line := p.fileLine(obj) + if file == p.prevFile { + // common case: write line delta + // delta == 0 means different file or no line change + delta := line - p.prevLine + p.int(delta) + if delta == 0 { + p.int(-1) // -1 means no file change + } + } else { + // different file + p.int(0) + // Encode filename as length of common prefix with previous + // filename, followed by (possibly empty) suffix. Filenames + // frequently share path prefixes, so this can save a lot + // of space and make export data size less dependent on file + // path length. The suffix is unlikely to be empty because + // file names tend to end in ".go". + n := commonPrefixLen(p.prevFile, file) + p.int(n) // n >= 0 + p.string(file[n:]) // write suffix only + p.prevFile = file + p.int(line) + } + p.prevLine = line +} + +func (p *exporter) fileLine(obj types.Object) (file string, line int) { + if p.fset != nil { + pos := p.fset.Position(obj.Pos()) + file = pos.Filename + line = pos.Line + } + return +} + +func commonPrefixLen(a, b string) int { + if len(a) > len(b) { + a, b = b, a + } + // len(a) <= len(b) + i := 0 + for i < len(a) && a[i] == b[i] { + i++ + } + return i +} + +func (p *exporter) qualifiedName(obj types.Object) { + p.string(obj.Name()) + p.pkg(obj.Pkg(), false) +} + +func (p *exporter) typ(t types.Type) { + if t == nil { + panic(internalError("nil type")) + } + + // Possible optimization: Anonymous pointer types *T where + // T is a named type are common. We could canonicalize all + // such types *T to a single type PT = *T. This would lead + // to at most one *T entry in typIndex, and all future *T's + // would be encoded as the respective index directly. Would + // save 1 byte (pointerTag) per *T and reduce the typIndex + // size (at the cost of a canonicalization map). We can do + // this later, without encoding format change. + + // if we saw the type before, write its index (>= 0) + if i, ok := p.typIndex[t]; ok { + p.index('T', i) + return + } + + // otherwise, remember the type, write the type tag (< 0) and type data + if trackAllTypes { + if trace { + p.tracef("T%d = {>\n", len(p.typIndex)) + defer p.tracef("<\n} ") + } + p.typIndex[t] = len(p.typIndex) + } + + switch t := t.(type) { + case *types.Named: + if !trackAllTypes { + // if we don't track all types, track named types now + p.typIndex[t] = len(p.typIndex) + } + + p.tag(namedTag) + p.pos(t.Obj()) + p.qualifiedName(t.Obj()) + p.typ(t.Underlying()) + if !types.IsInterface(t) { + p.assocMethods(t) + } + + case *types.Array: + p.tag(arrayTag) + p.int64(t.Len()) + p.typ(t.Elem()) + + case *types.Slice: + p.tag(sliceTag) + p.typ(t.Elem()) + + case *dddSlice: + p.tag(dddTag) + p.typ(t.elem) + + case *types.Struct: + p.tag(structTag) + p.fieldList(t) + + case *types.Pointer: + p.tag(pointerTag) + p.typ(t.Elem()) + + case *types.Signature: + p.tag(signatureTag) + p.paramList(t.Params(), t.Variadic()) + p.paramList(t.Results(), false) + + case *types.Interface: + p.tag(interfaceTag) + p.iface(t) + + case *types.Map: + p.tag(mapTag) + p.typ(t.Key()) + p.typ(t.Elem()) + + case *types.Chan: + p.tag(chanTag) + p.int(int(3 - t.Dir())) // hack + p.typ(t.Elem()) + + default: + panic(internalErrorf("unexpected type %T: %s", t, t)) + } +} + +func (p *exporter) assocMethods(named *types.Named) { + // Sort methods (for determinism). + var methods []*types.Func + for i := 0; i < named.NumMethods(); i++ { + methods = append(methods, named.Method(i)) + } + sort.Sort(methodsByName(methods)) + + p.int(len(methods)) + + if trace && methods != nil { + p.tracef("associated methods {>\n") + } + + for i, m := range methods { + if trace && i > 0 { + p.tracef("\n") + } + + p.pos(m) + name := m.Name() + p.string(name) + if !exported(name) { + p.pkg(m.Pkg(), false) + } + + sig := m.Type().(*types.Signature) + p.paramList(types.NewTuple(sig.Recv()), false) + p.paramList(sig.Params(), sig.Variadic()) + p.paramList(sig.Results(), false) + p.int(0) // dummy value for go:nointerface pragma - ignored by importer + } + + if trace && methods != nil { + p.tracef("<\n} ") + } +} + +type methodsByName []*types.Func + +func (x methodsByName) Len() int { return len(x) } +func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } + +func (p *exporter) fieldList(t *types.Struct) { + if trace && t.NumFields() > 0 { + p.tracef("fields {>\n") + defer p.tracef("<\n} ") + } + + p.int(t.NumFields()) + for i := 0; i < t.NumFields(); i++ { + if trace && i > 0 { + p.tracef("\n") + } + p.field(t.Field(i)) + p.string(t.Tag(i)) + } +} + +func (p *exporter) field(f *types.Var) { + if !f.IsField() { + panic(internalError("field expected")) + } + + p.pos(f) + p.fieldName(f) + p.typ(f.Type()) +} + +func (p *exporter) iface(t *types.Interface) { + // TODO(gri): enable importer to load embedded interfaces, + // then emit Embeddeds and ExplicitMethods separately here. + p.int(0) + + n := t.NumMethods() + if trace && n > 0 { + p.tracef("methods {>\n") + defer p.tracef("<\n} ") + } + p.int(n) + for i := 0; i < n; i++ { + if trace && i > 0 { + p.tracef("\n") + } + p.method(t.Method(i)) + } +} + +func (p *exporter) method(m *types.Func) { + sig := m.Type().(*types.Signature) + if sig.Recv() == nil { + panic(internalError("method expected")) + } + + p.pos(m) + p.string(m.Name()) + if m.Name() != "_" && !token.IsExported(m.Name()) { + p.pkg(m.Pkg(), false) + } + + // interface method; no need to encode receiver. + p.paramList(sig.Params(), sig.Variadic()) + p.paramList(sig.Results(), false) +} + +func (p *exporter) fieldName(f *types.Var) { + name := f.Name() + + if f.Anonymous() { + // anonymous field - we distinguish between 3 cases: + // 1) field name matches base type name and is exported + // 2) field name matches base type name and is not exported + // 3) field name doesn't match base type name (alias name) + bname := basetypeName(f.Type()) + if name == bname { + if token.IsExported(name) { + name = "" // 1) we don't need to know the field name or package + } else { + name = "?" // 2) use unexported name "?" to force package export + } + } else { + // 3) indicate alias and export name as is + // (this requires an extra "@" but this is a rare case) + p.string("@") + } + } + + p.string(name) + if name != "" && !token.IsExported(name) { + p.pkg(f.Pkg(), false) + } +} + +func basetypeName(typ types.Type) string { + switch typ := deref(typ).(type) { + case *types.Basic: + return typ.Name() + case *types.Named: + return typ.Obj().Name() + default: + return "" // unnamed type + } +} + +func (p *exporter) paramList(params *types.Tuple, variadic bool) { + // use negative length to indicate unnamed parameters + // (look at the first parameter only since either all + // names are present or all are absent) + n := params.Len() + if n > 0 && params.At(0).Name() == "" { + n = -n + } + p.int(n) + for i := 0; i < params.Len(); i++ { + q := params.At(i) + t := q.Type() + if variadic && i == params.Len()-1 { + t = &dddSlice{t.(*types.Slice).Elem()} + } + p.typ(t) + if n > 0 { + name := q.Name() + p.string(name) + if name != "_" { + p.pkg(q.Pkg(), false) + } + } + p.string("") // no compiler-specific info + } +} + +func (p *exporter) value(x constant.Value) { + if trace { + p.tracef("= ") + } + + switch x.Kind() { + case constant.Bool: + tag := falseTag + if constant.BoolVal(x) { + tag = trueTag + } + p.tag(tag) + + case constant.Int: + if v, exact := constant.Int64Val(x); exact { + // common case: x fits into an int64 - use compact encoding + p.tag(int64Tag) + p.int64(v) + return + } + // uncommon case: large x - use float encoding + // (powers of 2 will be encoded efficiently with exponent) + p.tag(floatTag) + p.float(constant.ToFloat(x)) + + case constant.Float: + p.tag(floatTag) + p.float(x) + + case constant.Complex: + p.tag(complexTag) + p.float(constant.Real(x)) + p.float(constant.Imag(x)) + + case constant.String: + p.tag(stringTag) + p.string(constant.StringVal(x)) + + case constant.Unknown: + // package contains type errors + p.tag(unknownTag) + + default: + panic(internalErrorf("unexpected value %v (%T)", x, x)) + } +} + +func (p *exporter) float(x constant.Value) { + if x.Kind() != constant.Float { + panic(internalErrorf("unexpected constant %v, want float", x)) + } + // extract sign (there is no -0) + sign := constant.Sign(x) + if sign == 0 { + // x == 0 + p.int(0) + return + } + // x != 0 + + var f big.Float + if v, exact := constant.Float64Val(x); exact { + // float64 + f.SetFloat64(v) + } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { + // TODO(gri): add big.Rat accessor to constant.Value. + r := valueToRat(num) + f.SetRat(r.Quo(r, valueToRat(denom))) + } else { + // Value too large to represent as a fraction => inaccessible. + // TODO(gri): add big.Float accessor to constant.Value. + f.SetFloat64(math.MaxFloat64) // FIXME + } + + // extract exponent such that 0.5 <= m < 1.0 + var m big.Float + exp := f.MantExp(&m) + + // extract mantissa as *big.Int + // - set exponent large enough so mant satisfies mant.IsInt() + // - get *big.Int from mant + m.SetMantExp(&m, int(m.MinPrec())) + mant, acc := m.Int(nil) + if acc != big.Exact { + panic(internalError("internal error")) + } + + p.int(sign) + p.int(exp) + p.string(string(mant.Bytes())) +} + +func valueToRat(x constant.Value) *big.Rat { + // Convert little-endian to big-endian. + // I can't believe this is necessary. + bytes := constant.Bytes(x) + for i := 0; i < len(bytes)/2; i++ { + bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] + } + return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) +} + +func (p *exporter) bool(b bool) bool { + if trace { + p.tracef("[") + defer p.tracef("= %v] ", b) + } + + x := 0 + if b { + x = 1 + } + p.int(x) + return b +} + +// ---------------------------------------------------------------------------- +// Low-level encoders + +func (p *exporter) index(marker byte, index int) { + if index < 0 { + panic(internalError("invalid index < 0")) + } + if debugFormat { + p.marker('t') + } + if trace { + p.tracef("%c%d ", marker, index) + } + p.rawInt64(int64(index)) +} + +func (p *exporter) tag(tag int) { + if tag >= 0 { + panic(internalError("invalid tag >= 0")) + } + if debugFormat { + p.marker('t') + } + if trace { + p.tracef("%s ", tagString[-tag]) + } + p.rawInt64(int64(tag)) +} + +func (p *exporter) int(x int) { + p.int64(int64(x)) +} + +func (p *exporter) int64(x int64) { + if debugFormat { + p.marker('i') + } + if trace { + p.tracef("%d ", x) + } + p.rawInt64(x) +} + +func (p *exporter) string(s string) { + if debugFormat { + p.marker('s') + } + if trace { + p.tracef("%q ", s) + } + // if we saw the string before, write its index (>= 0) + // (the empty string is mapped to 0) + if i, ok := p.strIndex[s]; ok { + p.rawInt64(int64(i)) + return + } + // otherwise, remember string and write its negative length and bytes + p.strIndex[s] = len(p.strIndex) + p.rawInt64(-int64(len(s))) + for i := 0; i < len(s); i++ { + p.rawByte(s[i]) + } +} + +// marker emits a marker byte and position information which makes +// it easy for a reader to detect if it is "out of sync". Used for +// debugFormat format only. +func (p *exporter) marker(m byte) { + p.rawByte(m) + // Enable this for help tracking down the location + // of an incorrect marker when running in debugFormat. + if false && trace { + p.tracef("#%d ", p.written) + } + p.rawInt64(int64(p.written)) +} + +// rawInt64 should only be used by low-level encoders. +func (p *exporter) rawInt64(x int64) { + var tmp [binary.MaxVarintLen64]byte + n := binary.PutVarint(tmp[:], x) + for i := 0; i < n; i++ { + p.rawByte(tmp[i]) + } +} + +// rawStringln should only be used to emit the initial version string. +func (p *exporter) rawStringln(s string) { + for i := 0; i < len(s); i++ { + p.rawByte(s[i]) + } + p.rawByte('\n') +} + +// rawByte is the bottleneck interface to write to p.out. +// rawByte escapes b as follows (any encoding does that +// hides '$'): +// +// '$' => '|' 'S' +// '|' => '|' '|' +// +// Necessary so other tools can find the end of the +// export data by searching for "$$". +// rawByte should only be used by low-level encoders. +func (p *exporter) rawByte(b byte) { + switch b { + case '$': + // write '$' as '|' 'S' + b = 'S' + fallthrough + case '|': + // write '|' as '|' '|' + p.out.WriteByte('|') + p.written++ + } + p.out.WriteByte(b) + p.written++ +} + +// tracef is like fmt.Printf but it rewrites the format string +// to take care of indentation. +func (p *exporter) tracef(format string, args ...interface{}) { + if strings.ContainsAny(format, "<>\n") { + var buf bytes.Buffer + for i := 0; i < len(format); i++ { + // no need to deal with runes + ch := format[i] + switch ch { + case '>': + p.indent++ + continue + case '<': + p.indent-- + continue + } + buf.WriteByte(ch) + if ch == '\n' { + for j := p.indent; j > 0; j-- { + buf.WriteString(". ") + } + } + } + format = buf.String() + } + fmt.Printf(format, args...) +} + +// Debugging support. +// (tagString is only used when tracing is enabled) +var tagString = [...]string{ + // Packages + -packageTag: "package", + + // Types + -namedTag: "named type", + -arrayTag: "array", + -sliceTag: "slice", + -dddTag: "ddd", + -structTag: "struct", + -pointerTag: "pointer", + -signatureTag: "signature", + -interfaceTag: "interface", + -mapTag: "map", + -chanTag: "chan", + + // Values + -falseTag: "false", + -trueTag: "true", + -int64Tag: "int64", + -floatTag: "float", + -fractionTag: "fraction", + -complexTag: "complex", + -stringTag: "string", + -unknownTag: "unknown", + + // Type aliases + -aliasTag: "alias", +} diff --git a/gopls/golang_org_x_tools/gcimporter/bimport.go b/gopls/golang_org_x_tools/gcimporter/bimport.go new file mode 100644 index 0000000..b85de01 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/bimport.go @@ -0,0 +1,1053 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go. + +package gcimporter + +import ( + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "go/types" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +type importer struct { + imports map[string]*types.Package + data []byte + importpath string + buf []byte // for reading strings + version int // export format version + + // object lists + strList []string // in order of appearance + pathList []string // in order of appearance + pkgList []*types.Package // in order of appearance + typList []types.Type // in order of appearance + interfaceList []*types.Interface // for delayed completion only + trackAllTypes bool + + // position encoding + posInfoFormat bool + prevFile string + prevLine int + fake fakeFileSet + + // debugging support + debugFormat bool + read int // bytes read +} + +// BImportData imports a package from the serialized package data +// and returns the number of bytes consumed and a reference to the package. +// If the export data version is not recognized or the format is otherwise +// compromised, an error is returned. +func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { + // catch panics and return them as errors + const currentVersion = 6 + version := -1 // unknown version + defer func() { + if e := recover(); e != nil { + // Return a (possibly nil or incomplete) package unchanged (see #16088). + if version > currentVersion { + err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) + } else { + err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + } + } + }() + + p := importer{ + imports: imports, + data: data, + importpath: path, + version: version, + strList: []string{""}, // empty string is mapped to 0 + pathList: []string{""}, // empty string is mapped to 0 + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*fileInfo), + }, + } + defer p.fake.setLines() // set lines for files in fset + + // read version info + var versionstr string + if b := p.rawByte(); b == 'c' || b == 'd' { + // Go1.7 encoding; first byte encodes low-level + // encoding format (compact vs debug). + // For backward-compatibility only (avoid problems with + // old installed packages). Newly compiled packages use + // the extensible format string. + // TODO(gri) Remove this support eventually; after Go1.8. + if b == 'd' { + p.debugFormat = true + } + p.trackAllTypes = p.rawByte() == 'a' + p.posInfoFormat = p.int() != 0 + versionstr = p.string() + if versionstr == "v1" { + version = 0 + } + } else { + // Go1.8 extensible encoding + // read version string and extract version number (ignore anything after the version number) + versionstr = p.rawStringln(b) + if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { + if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { + version = v + } + } + } + p.version = version + + // read version specific flags - extend as necessary + switch p.version { + // case currentVersion: + // ... + // fallthrough + case currentVersion, 5, 4, 3, 2, 1: + p.debugFormat = p.rawStringln(p.rawByte()) == "debug" + p.trackAllTypes = p.int() != 0 + p.posInfoFormat = p.int() != 0 + case 0: + // Go1.7 encoding format - nothing to do here + default: + errorf("unknown bexport format version %d (%q)", p.version, versionstr) + } + + // --- generic export data --- + + // populate typList with predeclared "known" types + p.typList = append(p.typList, predeclared()...) + + // read package data + pkg = p.pkg() + + // read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go) + objcount := 0 + for { + tag := p.tagOrIndex() + if tag == endTag { + break + } + p.obj(tag) + objcount++ + } + + // self-verification + if count := p.int(); count != objcount { + errorf("got %d objects; want %d", objcount, count) + } + + // ignore compiler-specific import data + + // complete interfaces + // TODO(gri) re-investigate if we still need to do this in a delayed fashion + for _, typ := range p.interfaceList { + typ.Complete() + } + + // record all referenced packages as imports + list := append(([]*types.Package)(nil), p.pkgList[1:]...) + sort.Sort(byPath(list)) + pkg.SetImports(list) + + // package was imported completely and without errors + pkg.MarkComplete() + + return p.read, pkg, nil +} + +func errorf(format string, args ...interface{}) { + panic(fmt.Sprintf(format, args...)) +} + +func (p *importer) pkg() *types.Package { + // if the package was seen before, i is its index (>= 0) + i := p.tagOrIndex() + if i >= 0 { + return p.pkgList[i] + } + + // otherwise, i is the package tag (< 0) + if i != packageTag { + errorf("unexpected package tag %d version %d", i, p.version) + } + + // read package data + name := p.string() + var path string + if p.version >= 5 { + path = p.path() + } else { + path = p.string() + } + if p.version >= 6 { + p.int() // package height; unused by go/types + } + + // we should never see an empty package name + if name == "" { + errorf("empty package name in import") + } + + // an empty path denotes the package we are currently importing; + // it must be the first package we see + if (path == "") != (len(p.pkgList) == 0) { + errorf("package path %q for pkg index %d", path, len(p.pkgList)) + } + + // if the package was imported before, use that one; otherwise create a new one + if path == "" { + path = p.importpath + } + pkg := p.imports[path] + if pkg == nil { + pkg = types.NewPackage(path, name) + p.imports[path] = pkg + } else if pkg.Name() != name { + errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path) + } + p.pkgList = append(p.pkgList, pkg) + + return pkg +} + +// objTag returns the tag value for each object kind. +func objTag(obj types.Object) int { + switch obj.(type) { + case *types.Const: + return constTag + case *types.TypeName: + return typeTag + case *types.Var: + return varTag + case *types.Func: + return funcTag + default: + errorf("unexpected object: %v (%T)", obj, obj) // panics + panic("unreachable") + } +} + +func sameObj(a, b types.Object) bool { + // Because unnamed types are not canonicalized, we cannot simply compare types for + // (pointer) identity. + // Ideally we'd check equality of constant values as well, but this is good enough. + return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type()) +} + +func (p *importer) declare(obj types.Object) { + pkg := obj.Pkg() + if alt := pkg.Scope().Insert(obj); alt != nil { + // This can only trigger if we import a (non-type) object a second time. + // Excluding type aliases, this cannot happen because 1) we only import a package + // once; and b) we ignore compiler-specific export data which may contain + // functions whose inlined function bodies refer to other functions that + // were already imported. + // However, type aliases require reexporting the original type, so we need + // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, + // method importer.obj, switch case importing functions). + // TODO(gri) review/update this comment once the gc compiler handles type aliases. + if !sameObj(obj, alt) { + errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) + } + } +} + +func (p *importer) obj(tag int) { + switch tag { + case constTag: + pos := p.pos() + pkg, name := p.qualifiedName() + typ := p.typ(nil, nil) + val := p.value() + p.declare(types.NewConst(pos, pkg, name, typ, val)) + + case aliasTag: + // TODO(gri) verify type alias hookup is correct + pos := p.pos() + pkg, name := p.qualifiedName() + typ := p.typ(nil, nil) + p.declare(types.NewTypeName(pos, pkg, name, typ)) + + case typeTag: + p.typ(nil, nil) + + case varTag: + pos := p.pos() + pkg, name := p.qualifiedName() + typ := p.typ(nil, nil) + p.declare(types.NewVar(pos, pkg, name, typ)) + + case funcTag: + pos := p.pos() + pkg, name := p.qualifiedName() + params, isddd := p.paramList() + result, _ := p.paramList() + sig := types.NewSignature(nil, params, result, isddd) + p.declare(types.NewFunc(pos, pkg, name, sig)) + + default: + errorf("unexpected object tag %d", tag) + } +} + +const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go + +func (p *importer) pos() token.Pos { + if !p.posInfoFormat { + return token.NoPos + } + + file := p.prevFile + line := p.prevLine + delta := p.int() + line += delta + if p.version >= 5 { + if delta == deltaNewFile { + if n := p.int(); n >= 0 { + // file changed + file = p.path() + line = n + } + } + } else { + if delta == 0 { + if n := p.int(); n >= 0 { + // file changed + file = p.prevFile[:n] + p.string() + line = p.int() + } + } + } + p.prevFile = file + p.prevLine = line + + return p.fake.pos(file, line, 0) +} + +// Synthesize a token.Pos +type fakeFileSet struct { + fset *token.FileSet + files map[string]*fileInfo +} + +type fileInfo struct { + file *token.File + lastline int +} + +const maxlines = 64 * 1024 + +func (s *fakeFileSet) pos(file string, line, column int) token.Pos { + // TODO(mdempsky): Make use of column. + + // Since we don't know the set of needed file positions, we reserve maxlines + // positions per file. We delay calling token.File.SetLines until all + // positions have been calculated (by way of fakeFileSet.setLines), so that + // we can avoid setting unnecessary lines. See also golang/go#46586. + f := s.files[file] + if f == nil { + f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)} + s.files[file] = f + } + if line > maxlines { + line = 1 + } + if line > f.lastline { + f.lastline = line + } + + // Return a fake position assuming that f.file consists only of newlines. + return token.Pos(f.file.Base() + line - 1) +} + +func (s *fakeFileSet) setLines() { + fakeLinesOnce.Do(func() { + fakeLines = make([]int, maxlines) + for i := range fakeLines { + fakeLines[i] = i + } + }) + for _, f := range s.files { + f.file.SetLines(fakeLines[:f.lastline]) + } +} + +var ( + fakeLines []int + fakeLinesOnce sync.Once +) + +func (p *importer) qualifiedName() (pkg *types.Package, name string) { + name = p.string() + pkg = p.pkg() + return +} + +func (p *importer) record(t types.Type) { + p.typList = append(p.typList, t) +} + +// A dddSlice is a types.Type representing ...T parameters. +// It only appears for parameter types and does not escape +// the importer. +type dddSlice struct { + elem types.Type +} + +func (t *dddSlice) Underlying() types.Type { return t } +func (t *dddSlice) String() string { return "..." + t.elem.String() } + +// parent is the package which declared the type; parent == nil means +// the package currently imported. The parent package is needed for +// exported struct fields and interface methods which don't contain +// explicit package information in the export data. +// +// A non-nil tname is used as the "owner" of the result type; i.e., +// the result type is the underlying type of tname. tname is used +// to give interface methods a named receiver type where possible. +func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type { + // if the type was seen before, i is its index (>= 0) + i := p.tagOrIndex() + if i >= 0 { + return p.typList[i] + } + + // otherwise, i is the type tag (< 0) + switch i { + case namedTag: + // read type object + pos := p.pos() + parent, name := p.qualifiedName() + scope := parent.Scope() + obj := scope.Lookup(name) + + // if the object doesn't exist yet, create and insert it + if obj == nil { + obj = types.NewTypeName(pos, parent, name, nil) + scope.Insert(obj) + } + + if _, ok := obj.(*types.TypeName); !ok { + errorf("pkg = %s, name = %s => %s", parent, name, obj) + } + + // associate new named type with obj if it doesn't exist yet + t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) + + // but record the existing type, if any + tname := obj.Type().(*types.Named) // tname is either t0 or the existing type + p.record(tname) + + // read underlying type + t0.SetUnderlying(p.typ(parent, t0)) + + // interfaces don't have associated methods + if types.IsInterface(t0) { + return tname + } + + // read associated methods + for i := p.int(); i > 0; i-- { + // TODO(gri) replace this with something closer to fieldName + pos := p.pos() + name := p.string() + if !exported(name) { + p.pkg() + } + + recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? + params, isddd := p.paramList() + result, _ := p.paramList() + p.int() // go:nointerface pragma - discarded + + sig := types.NewSignature(recv.At(0), params, result, isddd) + t0.AddMethod(types.NewFunc(pos, parent, name, sig)) + } + + return tname + + case arrayTag: + t := new(types.Array) + if p.trackAllTypes { + p.record(t) + } + + n := p.int64() + *t = *types.NewArray(p.typ(parent, nil), n) + return t + + case sliceTag: + t := new(types.Slice) + if p.trackAllTypes { + p.record(t) + } + + *t = *types.NewSlice(p.typ(parent, nil)) + return t + + case dddTag: + t := new(dddSlice) + if p.trackAllTypes { + p.record(t) + } + + t.elem = p.typ(parent, nil) + return t + + case structTag: + t := new(types.Struct) + if p.trackAllTypes { + p.record(t) + } + + *t = *types.NewStruct(p.fieldList(parent)) + return t + + case pointerTag: + t := new(types.Pointer) + if p.trackAllTypes { + p.record(t) + } + + *t = *types.NewPointer(p.typ(parent, nil)) + return t + + case signatureTag: + t := new(types.Signature) + if p.trackAllTypes { + p.record(t) + } + + params, isddd := p.paramList() + result, _ := p.paramList() + *t = *types.NewSignature(nil, params, result, isddd) + return t + + case interfaceTag: + // Create a dummy entry in the type list. This is safe because we + // cannot expect the interface type to appear in a cycle, as any + // such cycle must contain a named type which would have been + // first defined earlier. + // TODO(gri) Is this still true now that we have type aliases? + // See issue #23225. + n := len(p.typList) + if p.trackAllTypes { + p.record(nil) + } + + var embeddeds []types.Type + for n := p.int(); n > 0; n-- { + p.pos() + embeddeds = append(embeddeds, p.typ(parent, nil)) + } + + t := newInterface(p.methodList(parent, tname), embeddeds) + p.interfaceList = append(p.interfaceList, t) + if p.trackAllTypes { + p.typList[n] = t + } + return t + + case mapTag: + t := new(types.Map) + if p.trackAllTypes { + p.record(t) + } + + key := p.typ(parent, nil) + val := p.typ(parent, nil) + *t = *types.NewMap(key, val) + return t + + case chanTag: + t := new(types.Chan) + if p.trackAllTypes { + p.record(t) + } + + dir := chanDir(p.int()) + val := p.typ(parent, nil) + *t = *types.NewChan(dir, val) + return t + + default: + errorf("unexpected type tag %d", i) // panics + panic("unreachable") + } +} + +func chanDir(d int) types.ChanDir { + // tag values must match the constants in cmd/compile/internal/gc/go.go + switch d { + case 1 /* Crecv */ : + return types.RecvOnly + case 2 /* Csend */ : + return types.SendOnly + case 3 /* Cboth */ : + return types.SendRecv + default: + errorf("unexpected channel dir %d", d) + return 0 + } +} + +func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) { + if n := p.int(); n > 0 { + fields = make([]*types.Var, n) + tags = make([]string, n) + for i := range fields { + fields[i], tags[i] = p.field(parent) + } + } + return +} + +func (p *importer) field(parent *types.Package) (*types.Var, string) { + pos := p.pos() + pkg, name, alias := p.fieldName(parent) + typ := p.typ(parent, nil) + tag := p.string() + + anonymous := false + if name == "" { + // anonymous field - typ must be T or *T and T must be a type name + switch typ := deref(typ).(type) { + case *types.Basic: // basic types are named types + pkg = nil // // objects defined in Universe scope have no package + name = typ.Name() + case *types.Named: + name = typ.Obj().Name() + default: + errorf("named base type expected") + } + anonymous = true + } else if alias { + // anonymous field: we have an explicit name because it's an alias + anonymous = true + } + + return types.NewField(pos, pkg, name, typ, anonymous), tag +} + +func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) { + if n := p.int(); n > 0 { + methods = make([]*types.Func, n) + for i := range methods { + methods[i] = p.method(parent, baseType) + } + } + return +} + +func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func { + pos := p.pos() + pkg, name, _ := p.fieldName(parent) + // If we don't have a baseType, use a nil receiver. + // A receiver using the actual interface type (which + // we don't know yet) will be filled in when we call + // types.Interface.Complete. + var recv *types.Var + if baseType != nil { + recv = types.NewVar(token.NoPos, parent, "", baseType) + } + params, isddd := p.paramList() + result, _ := p.paramList() + sig := types.NewSignature(recv, params, result, isddd) + return types.NewFunc(pos, pkg, name, sig) +} + +func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) { + name = p.string() + pkg = parent + if pkg == nil { + // use the imported package instead + pkg = p.pkgList[0] + } + if p.version == 0 && name == "_" { + // version 0 didn't export a package for _ fields + return + } + switch name { + case "": + // 1) field name matches base type name and is exported: nothing to do + case "?": + // 2) field name matches base type name and is not exported: need package + name = "" + pkg = p.pkg() + case "@": + // 3) field name doesn't match type name (alias) + name = p.string() + alias = true + fallthrough + default: + if !exported(name) { + pkg = p.pkg() + } + } + return +} + +func (p *importer) paramList() (*types.Tuple, bool) { + n := p.int() + if n == 0 { + return nil, false + } + // negative length indicates unnamed parameters + named := true + if n < 0 { + n = -n + named = false + } + // n > 0 + params := make([]*types.Var, n) + isddd := false + for i := range params { + params[i], isddd = p.param(named) + } + return types.NewTuple(params...), isddd +} + +func (p *importer) param(named bool) (*types.Var, bool) { + t := p.typ(nil, nil) + td, isddd := t.(*dddSlice) + if isddd { + t = types.NewSlice(td.elem) + } + + var pkg *types.Package + var name string + if named { + name = p.string() + if name == "" { + errorf("expected named parameter") + } + if name != "_" { + pkg = p.pkg() + } + if i := strings.Index(name, "·"); i > 0 { + name = name[:i] // cut off gc-specific parameter numbering + } + } + + // read and discard compiler-specific info + p.string() + + return types.NewVar(token.NoPos, pkg, name, t), isddd +} + +func exported(name string) bool { + ch, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(ch) +} + +func (p *importer) value() constant.Value { + switch tag := p.tagOrIndex(); tag { + case falseTag: + return constant.MakeBool(false) + case trueTag: + return constant.MakeBool(true) + case int64Tag: + return constant.MakeInt64(p.int64()) + case floatTag: + return p.float() + case complexTag: + re := p.float() + im := p.float() + return constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) + case stringTag: + return constant.MakeString(p.string()) + case unknownTag: + return constant.MakeUnknown() + default: + errorf("unexpected value tag %d", tag) // panics + panic("unreachable") + } +} + +func (p *importer) float() constant.Value { + sign := p.int() + if sign == 0 { + return constant.MakeInt64(0) + } + + exp := p.int() + mant := []byte(p.string()) // big endian + + // remove leading 0's if any + for len(mant) > 0 && mant[0] == 0 { + mant = mant[1:] + } + + // convert to little endian + // TODO(gri) go/constant should have a more direct conversion function + // (e.g., once it supports a big.Float based implementation) + for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 { + mant[i], mant[j] = mant[j], mant[i] + } + + // adjust exponent (constant.MakeFromBytes creates an integer value, + // but mant represents the mantissa bits such that 0.5 <= mant < 1.0) + exp -= len(mant) << 3 + if len(mant) > 0 { + for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 { + exp++ + } + } + + x := constant.MakeFromBytes(mant) + switch { + case exp < 0: + d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) + x = constant.BinaryOp(x, token.QUO, d) + case exp > 0: + x = constant.Shift(x, token.SHL, uint(exp)) + } + + if sign < 0 { + x = constant.UnaryOp(token.SUB, x, 0) + } + return x +} + +// ---------------------------------------------------------------------------- +// Low-level decoders + +func (p *importer) tagOrIndex() int { + if p.debugFormat { + p.marker('t') + } + + return int(p.rawInt64()) +} + +func (p *importer) int() int { + x := p.int64() + if int64(int(x)) != x { + errorf("exported integer too large") + } + return int(x) +} + +func (p *importer) int64() int64 { + if p.debugFormat { + p.marker('i') + } + + return p.rawInt64() +} + +func (p *importer) path() string { + if p.debugFormat { + p.marker('p') + } + // if the path was seen before, i is its index (>= 0) + // (the empty string is at index 0) + i := p.rawInt64() + if i >= 0 { + return p.pathList[i] + } + // otherwise, i is the negative path length (< 0) + a := make([]string, -i) + for n := range a { + a[n] = p.string() + } + s := strings.Join(a, "/") + p.pathList = append(p.pathList, s) + return s +} + +func (p *importer) string() string { + if p.debugFormat { + p.marker('s') + } + // if the string was seen before, i is its index (>= 0) + // (the empty string is at index 0) + i := p.rawInt64() + if i >= 0 { + return p.strList[i] + } + // otherwise, i is the negative string length (< 0) + if n := int(-i); n <= cap(p.buf) { + p.buf = p.buf[:n] + } else { + p.buf = make([]byte, n) + } + for i := range p.buf { + p.buf[i] = p.rawByte() + } + s := string(p.buf) + p.strList = append(p.strList, s) + return s +} + +func (p *importer) marker(want byte) { + if got := p.rawByte(); got != want { + errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read) + } + + pos := p.read + if n := int(p.rawInt64()); n != pos { + errorf("incorrect position: got %d; want %d", n, pos) + } +} + +// rawInt64 should only be used by low-level decoders. +func (p *importer) rawInt64() int64 { + i, err := binary.ReadVarint(p) + if err != nil { + errorf("read error: %v", err) + } + return i +} + +// rawStringln should only be used to read the initial version string. +func (p *importer) rawStringln(b byte) string { + p.buf = p.buf[:0] + for b != '\n' { + p.buf = append(p.buf, b) + b = p.rawByte() + } + return string(p.buf) +} + +// needed for binary.ReadVarint in rawInt64 +func (p *importer) ReadByte() (byte, error) { + return p.rawByte(), nil +} + +// byte is the bottleneck interface for reading p.data. +// It unescapes '|' 'S' to '$' and '|' '|' to '|'. +// rawByte should only be used by low-level decoders. +func (p *importer) rawByte() byte { + b := p.data[0] + r := 1 + if b == '|' { + b = p.data[1] + r = 2 + switch b { + case 'S': + b = '$' + case '|': + // nothing to do + default: + errorf("unexpected escape sequence in export data") + } + } + p.data = p.data[r:] + p.read += r + return b + +} + +// ---------------------------------------------------------------------------- +// Export format + +// Tags. Must be < 0. +const ( + // Objects + packageTag = -(iota + 1) + constTag + typeTag + varTag + funcTag + endTag + + // Types + namedTag + arrayTag + sliceTag + dddTag + structTag + pointerTag + signatureTag + interfaceTag + mapTag + chanTag + + // Values + falseTag + trueTag + int64Tag + floatTag + fractionTag // not used by gc + complexTag + stringTag + nilTag // only used by gc (appears in exported inlined function bodies) + unknownTag // not used by gc (only appears in packages with errors) + + // Type aliases + aliasTag +) + +var predeclOnce sync.Once +var predecl []types.Type // initialized lazily + +func predeclared() []types.Type { + predeclOnce.Do(func() { + // initialize lazily to be sure that all + // elements have been initialized before + predecl = []types.Type{ // basic types + types.Typ[types.Bool], + types.Typ[types.Int], + types.Typ[types.Int8], + types.Typ[types.Int16], + types.Typ[types.Int32], + types.Typ[types.Int64], + types.Typ[types.Uint], + types.Typ[types.Uint8], + types.Typ[types.Uint16], + types.Typ[types.Uint32], + types.Typ[types.Uint64], + types.Typ[types.Uintptr], + types.Typ[types.Float32], + types.Typ[types.Float64], + types.Typ[types.Complex64], + types.Typ[types.Complex128], + types.Typ[types.String], + + // basic type aliases + types.Universe.Lookup("byte").Type(), + types.Universe.Lookup("rune").Type(), + + // error + types.Universe.Lookup("error").Type(), + + // untyped types + types.Typ[types.UntypedBool], + types.Typ[types.UntypedInt], + types.Typ[types.UntypedRune], + types.Typ[types.UntypedFloat], + types.Typ[types.UntypedComplex], + types.Typ[types.UntypedString], + types.Typ[types.UntypedNil], + + // package unsafe + types.Typ[types.UnsafePointer], + + // invalid type + types.Typ[types.Invalid], // only appears in packages with errors + + // used internally by gc; never used by this package or in .a files + anyType{}, + } + predecl = append(predecl, additionalPredeclared()...) + }) + return predecl +} + +type anyType struct{} + +func (t anyType) Underlying() types.Type { return t } +func (t anyType) String() string { return "any" } diff --git a/gopls/golang_org_x_tools/gcimporter/exportdata.go b/gopls/golang_org_x_tools/gcimporter/exportdata.go new file mode 100644 index 0000000..f6437fe --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/exportdata.go @@ -0,0 +1,99 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go. + +// This file implements FindExportData. + +package gcimporter + +import ( + "bufio" + "fmt" + "io" + "strconv" + "strings" +) + +func readGopackHeader(r *bufio.Reader) (name string, size int64, err error) { + // See $GOROOT/include/ar.h. + hdr := make([]byte, 16+12+6+6+8+10+2) + _, err = io.ReadFull(r, hdr) + if err != nil { + return + } + // leave for debugging + if false { + fmt.Printf("header: %s", hdr) + } + s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) + length, err := strconv.Atoi(s) + size = int64(length) + if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { + err = fmt.Errorf("invalid archive header") + return + } + name = strings.TrimSpace(string(hdr[:16])) + return +} + +// FindExportData positions the reader r at the beginning of the +// export data section of an underlying GC-created object/archive +// file by reading from it. The reader must be positioned at the +// start of the file before calling this function. The hdr result +// is the string before the export data, either "$$" or "$$B". +// The size result is the length of the export data in bytes, or -1 if not known. +func FindExportData(r *bufio.Reader) (hdr string, size int64, err error) { + // Read first line to make sure this is an object file. + line, err := r.ReadSlice('\n') + if err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + + if string(line) == "!\n" { + // Archive file. Scan to __.PKGDEF. + var name string + if name, size, err = readGopackHeader(r); err != nil { + return + } + + // First entry should be __.PKGDEF. + if name != "__.PKGDEF" { + err = fmt.Errorf("go archive is missing __.PKGDEF") + return + } + + // Read first line of __.PKGDEF data, so that line + // is once again the first line of the input. + if line, err = r.ReadSlice('\n'); err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + size -= int64(len(line)) + } + + // Now at __.PKGDEF in archive or still at beginning of file. + // Either way, line should begin with "go object ". + if !strings.HasPrefix(string(line), "go object ") { + err = fmt.Errorf("not a Go object file") + return + } + + // Skip over object header to export data. + // Begins after first line starting with $$. + for line[0] != '$' { + if line, err = r.ReadSlice('\n'); err != nil { + err = fmt.Errorf("can't find export data (%v)", err) + return + } + size -= int64(len(line)) + } + hdr = string(line) + if size < 0 { + size = -1 + } + + return +} diff --git a/gopls/golang_org_x_tools/gcimporter/gcimporter.go b/gopls/golang_org_x_tools/gcimporter/gcimporter.go new file mode 100644 index 0000000..4a744c5 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/gcimporter.go @@ -0,0 +1,1160 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a modified copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go, +// but it also contains the original source-based importer code for Go1.6. +// Once we stop supporting 1.6, we can remove that code. + +// Package gcimporter provides various functions for reading +// gc-generated object files that can be used to implement the +// Importer interface defined by the Go 1.5 standard library package. +package gcimporter // import "github.com/visualfc/gotools/gopls/golang_org_x_tools/gcimporter" + +import ( + "bufio" + "errors" + "fmt" + "go/build" + "go/constant" + "go/token" + "go/types" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "sort" + "strconv" + "strings" + "text/scanner" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/goroot" +) + +const ( + // Enable debug during development: it adds some additional checks, and + // prevents errors from being recovered. + debug = false + + // If trace is set, debugging output is printed to std out. + trace = false +) + +func lookupGorootExport(pkgpath, srcRoot, srcDir string) (string, bool) { + pkgpath = filepath.ToSlash(pkgpath) + m, err := goroot.PkgfileMap() + if err != nil { + return "", false + } + if export, ok := m[pkgpath]; ok { + return export, true + } + vendorPrefix := "vendor" + if strings.HasPrefix(srcDir, filepath.Join(srcRoot, "cmd")) { + vendorPrefix = path.Join("cmd", vendorPrefix) + } + pkgpath = path.Join(vendorPrefix, pkgpath) + fmt.Fprintln(os.Stderr, "looking up ", pkgpath) + export, ok := m[pkgpath] + return export, ok +} + +var pkgExts = [...]string{".a", ".o"} + +// FindPkg returns the filename and unique package id for an import +// path based on package information provided by build.Import (using +// the build.Default build.Context). A relative srcDir is interpreted +// relative to the current working directory. +// If no file was found, an empty filename is returned. +func FindPkg(path, srcDir string) (filename, id string) { + if path == "" { + return + } + + var noext string + switch { + default: + // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" + // Don't require the source files to be present. + if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 + srcDir = abs + } + bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) + if bp.PkgObj == "" { + var ok bool + if bp.Goroot { + filename, ok = lookupGorootExport(path, bp.SrcRoot, srcDir) + } + if !ok { + id = path // make sure we have an id to print in error message + return + } + } else { + noext = strings.TrimSuffix(bp.PkgObj, ".a") + id = bp.ImportPath + } + + case build.IsLocalImport(path): + // "./x" -> "/this/directory/x.ext", "/this/directory/x" + noext = filepath.Join(srcDir, path) + id = noext + + case filepath.IsAbs(path): + // for completeness only - go/build.Import + // does not support absolute imports + // "/x" -> "/x.ext", "/x" + noext = path + id = path + } + + if false { // for debugging + if path != id { + fmt.Printf("%s -> %s\n", path, id) + } + } + + if filename != "" { + if f, err := os.Stat(filename); err == nil && !f.IsDir() { + return + } + } + + // try extensions + for _, ext := range pkgExts { + filename = noext + ext + if f, err := os.Stat(filename); err == nil && !f.IsDir() { + return + } + } + + filename = "" // not found + return +} + +// ImportData imports a package by reading the gc-generated export data, +// adds the corresponding package object to the packages map indexed by id, +// and returns the object. +// +// The packages map must contains all packages already imported. The data +// reader position must be the beginning of the export data section. The +// filename is only used in error messages. +// +// If packages[id] contains the completely imported package, that package +// can be used directly, and there is no need to call this function (but +// there is also no harm but for extra time used). +func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) { + // support for parser error handling + defer func() { + switch r := recover().(type) { + case nil: + // nothing to do + case importError: + err = r + default: + panic(r) // internal error + } + }() + + var p parser + p.init(filename, id, data, packages) + pkg = p.parseExport() + + return +} + +// Import imports a gc-generated package given its import path and srcDir, adds +// the corresponding package object to the packages map, and returns the object. +// The packages map must contain all packages already imported. +func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) { + var rc io.ReadCloser + var filename, id string + if lookup != nil { + // With custom lookup specified, assume that caller has + // converted path to a canonical import path for use in the map. + if path == "unsafe" { + return types.Unsafe, nil + } + id = path + + // No need to re-import if the package was imported completely before. + if pkg = packages[id]; pkg != nil && pkg.Complete() { + return + } + f, err := lookup(path) + if err != nil { + return nil, err + } + rc = f + } else { + filename, id = FindPkg(path, srcDir) + if filename == "" { + if path == "unsafe" { + return types.Unsafe, nil + } + return nil, fmt.Errorf("can't find import: %q", id) + } + + // no need to re-import if the package was imported completely before + if pkg = packages[id]; pkg != nil && pkg.Complete() { + return + } + + // open file + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + // add file name to error + err = fmt.Errorf("%s: %v", filename, err) + } + }() + rc = f + } + defer rc.Close() + + var hdr string + var size int64 + buf := bufio.NewReader(rc) + if hdr, size, err = FindExportData(buf); err != nil { + return + } + + switch hdr { + case "$$\n": + // Work-around if we don't have a filename; happens only if lookup != nil. + // Either way, the filename is only needed for importer error messages, so + // this is fine. + if filename == "" { + filename = path + } + return ImportData(packages, filename, id, buf) + + case "$$B\n": + var data []byte + data, err = ioutil.ReadAll(buf) + if err != nil { + break + } + + // TODO(gri): allow clients of go/importer to provide a FileSet. + // Or, define a new standard go/types/gcexportdata package. + fset := token.NewFileSet() + + // The indexed export format starts with an 'i'; the older + // binary export format starts with a 'c', 'd', or 'v' + // (from "version"). Select appropriate importer. + if len(data) > 0 { + switch data[0] { + case 'i': + _, pkg, err := IImportData(fset, packages, data[1:], id) + return pkg, err + + case 'v', 'c', 'd': + _, pkg, err := BImportData(fset, packages, data, id) + return pkg, err + + case 'u': + _, pkg, err := UImportData(fset, packages, data[1:size], id) + return pkg, err + + default: + l := len(data) + if l > 10 { + l = 10 + } + return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), id) + } + } + + default: + err = fmt.Errorf("unknown export data header: %q", hdr) + } + + return +} + +// ---------------------------------------------------------------------------- +// Parser + +// TODO(gri) Imported objects don't have position information. +// Ideally use the debug table line info; alternatively +// create some fake position (or the position of the +// import). That way error messages referring to imported +// objects can print meaningful information. + +// parser parses the exports inside a gc compiler-produced +// object/archive file and populates its scope with the results. +type parser struct { + scanner scanner.Scanner + tok rune // current token + lit string // literal string; only valid for Ident, Int, String tokens + id string // package id of imported package + sharedPkgs map[string]*types.Package // package id -> package object (across importer) + localPkgs map[string]*types.Package // package id -> package object (just this package) +} + +func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) { + p.scanner.Init(src) + p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } + p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments + p.scanner.Whitespace = 1<<'\t' | 1<<' ' + p.scanner.Filename = filename // for good error messages + p.next() + p.id = id + p.sharedPkgs = packages + if debug { + // check consistency of packages map + for _, pkg := range packages { + if pkg.Name() == "" { + fmt.Printf("no package name for %s\n", pkg.Path()) + } + } + } +} + +func (p *parser) next() { + p.tok = p.scanner.Scan() + switch p.tok { + case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·': + p.lit = p.scanner.TokenText() + default: + p.lit = "" + } + if debug { + fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) + } +} + +func declTypeName(pkg *types.Package, name string) *types.TypeName { + scope := pkg.Scope() + if obj := scope.Lookup(name); obj != nil { + return obj.(*types.TypeName) + } + obj := types.NewTypeName(token.NoPos, pkg, name, nil) + // a named type may be referred to before the underlying type + // is known - set it up + types.NewNamed(obj, nil, nil) + scope.Insert(obj) + return obj +} + +// ---------------------------------------------------------------------------- +// Error handling + +// Internal errors are boxed as importErrors. +type importError struct { + pos scanner.Position + err error +} + +func (e importError) Error() string { + return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) +} + +func (p *parser) error(err interface{}) { + if s, ok := err.(string); ok { + err = errors.New(s) + } + // panic with a runtime.Error if err is not an error + panic(importError{p.scanner.Pos(), err.(error)}) +} + +func (p *parser) errorf(format string, args ...interface{}) { + p.error(fmt.Sprintf(format, args...)) +} + +func (p *parser) expect(tok rune) string { + lit := p.lit + if p.tok != tok { + p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) + } + p.next() + return lit +} + +func (p *parser) expectSpecial(tok string) { + sep := 'x' // not white space + i := 0 + for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' { + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + i++ + } + if i < len(tok) { + p.errorf("expected %q, got %q", tok, tok[0:i]) + } +} + +func (p *parser) expectKeyword(keyword string) { + lit := p.expect(scanner.Ident) + if lit != keyword { + p.errorf("expected keyword %s, got %q", keyword, lit) + } +} + +// ---------------------------------------------------------------------------- +// Qualified and unqualified names + +// parsePackageID parses a PackageId: +// +// PackageId = string_lit . +func (p *parser) parsePackageID() string { + id, err := strconv.Unquote(p.expect(scanner.String)) + if err != nil { + p.error(err) + } + // id == "" stands for the imported package id + // (only known at time of package installation) + if id == "" { + id = p.id + } + return id +} + +// parsePackageName parse a PackageName: +// +// PackageName = ident . +func (p *parser) parsePackageName() string { + return p.expect(scanner.Ident) +} + +// parseDotIdent parses a dotIdentifier: +// +// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . +func (p *parser) parseDotIdent() string { + ident := "" + if p.tok != scanner.Int { + sep := 'x' // not white space + for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' { + ident += p.lit + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + } + } + if ident == "" { + p.expect(scanner.Ident) // use expect() for error handling + } + return ident +} + +// parseQualifiedName parses a QualifiedName: +// +// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . +func (p *parser) parseQualifiedName() (id, name string) { + p.expect('@') + id = p.parsePackageID() + p.expect('.') + // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields. + if p.tok == '?' { + p.next() + } else { + name = p.parseDotIdent() + } + return +} + +// getPkg returns the package for a given id. If the package is +// not found, create the package and add it to the p.localPkgs +// and p.sharedPkgs maps. name is the (expected) name of the +// package. If name == "", the package name is expected to be +// set later via an import clause in the export data. +// +// id identifies a package, usually by a canonical package path like +// "encoding/json" but possibly by a non-canonical import path like +// "./json". +func (p *parser) getPkg(id, name string) *types.Package { + // package unsafe is not in the packages maps - handle explicitly + if id == "unsafe" { + return types.Unsafe + } + + pkg := p.localPkgs[id] + if pkg == nil { + // first import of id from this package + pkg = p.sharedPkgs[id] + if pkg == nil { + // first import of id by this importer; + // add (possibly unnamed) pkg to shared packages + pkg = types.NewPackage(id, name) + p.sharedPkgs[id] = pkg + } + // add (possibly unnamed) pkg to local packages + if p.localPkgs == nil { + p.localPkgs = make(map[string]*types.Package) + } + p.localPkgs[id] = pkg + } else if name != "" { + // package exists already and we have an expected package name; + // make sure names match or set package name if necessary + if pname := pkg.Name(); pname == "" { + pkg.SetName(name) + } else if pname != name { + p.errorf("%s package name mismatch: %s (given) vs %s (expected)", id, pname, name) + } + } + return pkg +} + +// parseExportedName is like parseQualifiedName, but +// the package id is resolved to an imported *types.Package. +func (p *parser) parseExportedName() (pkg *types.Package, name string) { + id, name := p.parseQualifiedName() + pkg = p.getPkg(id, "") + return +} + +// ---------------------------------------------------------------------------- +// Types + +// parseBasicType parses a BasicType: +// +// BasicType = identifier . +func (p *parser) parseBasicType() types.Type { + id := p.expect(scanner.Ident) + obj := types.Universe.Lookup(id) + if obj, ok := obj.(*types.TypeName); ok { + return obj.Type() + } + p.errorf("not a basic type: %s", id) + return nil +} + +// parseArrayType parses an ArrayType: +// +// ArrayType = "[" int_lit "]" Type . +func (p *parser) parseArrayType(parent *types.Package) types.Type { + // "[" already consumed and lookahead known not to be "]" + lit := p.expect(scanner.Int) + p.expect(']') + elem := p.parseType(parent) + n, err := strconv.ParseInt(lit, 10, 64) + if err != nil { + p.error(err) + } + return types.NewArray(elem, n) +} + +// parseMapType parses a MapType: +// +// MapType = "map" "[" Type "]" Type . +func (p *parser) parseMapType(parent *types.Package) types.Type { + p.expectKeyword("map") + p.expect('[') + key := p.parseType(parent) + p.expect(']') + elem := p.parseType(parent) + return types.NewMap(key, elem) +} + +// parseName parses a Name: +// +// Name = identifier | "?" | QualifiedName . +// +// For unqualified and anonymous names, the returned package is the parent +// package unless parent == nil, in which case the returned package is the +// package being imported. (The parent package is not nil if the name +// is an unqualified struct field or interface method name belonging to a +// type declared in another package.) +// +// For qualified names, the returned package is nil (and not created if +// it doesn't exist yet) unless materializePkg is set (which creates an +// unnamed package with valid package path). In the latter case, a +// subsequent import clause is expected to provide a name for the package. +func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) { + pkg = parent + if pkg == nil { + pkg = p.sharedPkgs[p.id] + } + switch p.tok { + case scanner.Ident: + name = p.lit + p.next() + case '?': + // anonymous + p.next() + case '@': + // exported name prefixed with package path + pkg = nil + var id string + id, name = p.parseQualifiedName() + if materializePkg { + pkg = p.getPkg(id, "") + } + default: + p.error("name expected") + } + return +} + +func deref(typ types.Type) types.Type { + if p, _ := typ.(*types.Pointer); p != nil { + return p.Elem() + } + return typ +} + +// parseField parses a Field: +// +// Field = Name Type [ string_lit ] . +func (p *parser) parseField(parent *types.Package) (*types.Var, string) { + pkg, name := p.parseName(parent, true) + + if name == "_" { + // Blank fields should be package-qualified because they + // are unexported identifiers, but gc does not qualify them. + // Assuming that the ident belongs to the current package + // causes types to change during re-exporting, leading + // to spurious "can't assign A to B" errors from go/types. + // As a workaround, pretend all blank fields belong + // to the same unique dummy package. + const blankpkg = "<_>" + pkg = p.getPkg(blankpkg, blankpkg) + } + + typ := p.parseType(parent) + anonymous := false + if name == "" { + // anonymous field - typ must be T or *T and T must be a type name + switch typ := deref(typ).(type) { + case *types.Basic: // basic types are named types + pkg = nil // objects defined in Universe scope have no package + name = typ.Name() + case *types.Named: + name = typ.Obj().Name() + default: + p.errorf("anonymous field expected") + } + anonymous = true + } + tag := "" + if p.tok == scanner.String { + s := p.expect(scanner.String) + var err error + tag, err = strconv.Unquote(s) + if err != nil { + p.errorf("invalid struct tag %s: %s", s, err) + } + } + return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag +} + +// parseStructType parses a StructType: +// +// StructType = "struct" "{" [ FieldList ] "}" . +// FieldList = Field { ";" Field } . +func (p *parser) parseStructType(parent *types.Package) types.Type { + var fields []*types.Var + var tags []string + + p.expectKeyword("struct") + p.expect('{') + for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { + if i > 0 { + p.expect(';') + } + fld, tag := p.parseField(parent) + if tag != "" && tags == nil { + tags = make([]string, i) + } + if tags != nil { + tags = append(tags, tag) + } + fields = append(fields, fld) + } + p.expect('}') + + return types.NewStruct(fields, tags) +} + +// parseParameter parses a Parameter: +// +// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . +func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { + _, name := p.parseName(nil, false) + // remove gc-specific parameter numbering + if i := strings.Index(name, "·"); i >= 0 { + name = name[:i] + } + if p.tok == '.' { + p.expectSpecial("...") + isVariadic = true + } + typ := p.parseType(nil) + if isVariadic { + typ = types.NewSlice(typ) + } + // ignore argument tag (e.g. "noescape") + if p.tok == scanner.String { + p.next() + } + // TODO(gri) should we provide a package? + par = types.NewVar(token.NoPos, nil, name, typ) + return +} + +// parseParameters parses a Parameters: +// +// Parameters = "(" [ ParameterList ] ")" . +// ParameterList = { Parameter "," } Parameter . +func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) { + p.expect('(') + for p.tok != ')' && p.tok != scanner.EOF { + if len(list) > 0 { + p.expect(',') + } + par, variadic := p.parseParameter() + list = append(list, par) + if variadic { + if isVariadic { + p.error("... not on final argument") + } + isVariadic = true + } + } + p.expect(')') + + return +} + +// parseSignature parses a Signature: +// +// Signature = Parameters [ Result ] . +// Result = Type | Parameters . +func (p *parser) parseSignature(recv *types.Var) *types.Signature { + params, isVariadic := p.parseParameters() + + // optional result type + var results []*types.Var + if p.tok == '(' { + var variadic bool + results, variadic = p.parseParameters() + if variadic { + p.error("... not permitted on result type") + } + } + + return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) +} + +// parseInterfaceType parses an InterfaceType: +// +// InterfaceType = "interface" "{" [ MethodList ] "}" . +// MethodList = Method { ";" Method } . +// Method = Name Signature . +// +// The methods of embedded interfaces are always "inlined" +// by the compiler and thus embedded interfaces are never +// visible in the export data. +func (p *parser) parseInterfaceType(parent *types.Package) types.Type { + var methods []*types.Func + + p.expectKeyword("interface") + p.expect('{') + for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { + if i > 0 { + p.expect(';') + } + pkg, name := p.parseName(parent, true) + sig := p.parseSignature(nil) + methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig)) + } + p.expect('}') + + // Complete requires the type's embedded interfaces to be fully defined, + // but we do not define any + return newInterface(methods, nil).Complete() +} + +// parseChanType parses a ChanType: +// +// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . +func (p *parser) parseChanType(parent *types.Package) types.Type { + dir := types.SendRecv + if p.tok == scanner.Ident { + p.expectKeyword("chan") + if p.tok == '<' { + p.expectSpecial("<-") + dir = types.SendOnly + } + } else { + p.expectSpecial("<-") + p.expectKeyword("chan") + dir = types.RecvOnly + } + elem := p.parseType(parent) + return types.NewChan(dir, elem) +} + +// parseType parses a Type: +// +// Type = +// BasicType | TypeName | ArrayType | SliceType | StructType | +// PointerType | FuncType | InterfaceType | MapType | ChanType | +// "(" Type ")" . +// +// BasicType = ident . +// TypeName = ExportedName . +// SliceType = "[" "]" Type . +// PointerType = "*" Type . +// FuncType = "func" Signature . +func (p *parser) parseType(parent *types.Package) types.Type { + switch p.tok { + case scanner.Ident: + switch p.lit { + default: + return p.parseBasicType() + case "struct": + return p.parseStructType(parent) + case "func": + // FuncType + p.next() + return p.parseSignature(nil) + case "interface": + return p.parseInterfaceType(parent) + case "map": + return p.parseMapType(parent) + case "chan": + return p.parseChanType(parent) + } + case '@': + // TypeName + pkg, name := p.parseExportedName() + return declTypeName(pkg, name).Type() + case '[': + p.next() // look ahead + if p.tok == ']' { + // SliceType + p.next() + return types.NewSlice(p.parseType(parent)) + } + return p.parseArrayType(parent) + case '*': + // PointerType + p.next() + return types.NewPointer(p.parseType(parent)) + case '<': + return p.parseChanType(parent) + case '(': + // "(" Type ")" + p.next() + typ := p.parseType(parent) + p.expect(')') + return typ + } + p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit) + return nil +} + +// ---------------------------------------------------------------------------- +// Declarations + +// parseImportDecl parses an ImportDecl: +// +// ImportDecl = "import" PackageName PackageId . +func (p *parser) parseImportDecl() { + p.expectKeyword("import") + name := p.parsePackageName() + p.getPkg(p.parsePackageID(), name) +} + +// parseInt parses an int_lit: +// +// int_lit = [ "+" | "-" ] { "0" ... "9" } . +func (p *parser) parseInt() string { + s := "" + switch p.tok { + case '-': + s = "-" + p.next() + case '+': + p.next() + } + return s + p.expect(scanner.Int) +} + +// parseNumber parses a number: +// +// number = int_lit [ "p" int_lit ] . +func (p *parser) parseNumber() (typ *types.Basic, val constant.Value) { + // mantissa + mant := constant.MakeFromLiteral(p.parseInt(), token.INT, 0) + if mant == nil { + panic("invalid mantissa") + } + + if p.lit == "p" { + // exponent (base 2) + p.next() + exp, err := strconv.ParseInt(p.parseInt(), 10, 0) + if err != nil { + p.error(err) + } + if exp < 0 { + denom := constant.MakeInt64(1) + denom = constant.Shift(denom, token.SHL, uint(-exp)) + typ = types.Typ[types.UntypedFloat] + val = constant.BinaryOp(mant, token.QUO, denom) + return + } + if exp > 0 { + mant = constant.Shift(mant, token.SHL, uint(exp)) + } + typ = types.Typ[types.UntypedFloat] + val = mant + return + } + + typ = types.Typ[types.UntypedInt] + val = mant + return +} + +// parseConstDecl parses a ConstDecl: +// +// ConstDecl = "const" ExportedName [ Type ] "=" Literal . +// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . +// bool_lit = "true" | "false" . +// complex_lit = "(" float_lit "+" float_lit "i" ")" . +// rune_lit = "(" int_lit "+" int_lit ")" . +// string_lit = `"` { unicode_char } `"` . +func (p *parser) parseConstDecl() { + p.expectKeyword("const") + pkg, name := p.parseExportedName() + + var typ0 types.Type + if p.tok != '=' { + // constant types are never structured - no need for parent type + typ0 = p.parseType(nil) + } + + p.expect('=') + var typ types.Type + var val constant.Value + switch p.tok { + case scanner.Ident: + // bool_lit + if p.lit != "true" && p.lit != "false" { + p.error("expected true or false") + } + typ = types.Typ[types.UntypedBool] + val = constant.MakeBool(p.lit == "true") + p.next() + + case '-', scanner.Int: + // int_lit + typ, val = p.parseNumber() + + case '(': + // complex_lit or rune_lit + p.next() + if p.tok == scanner.Char { + p.next() + p.expect('+') + typ = types.Typ[types.UntypedRune] + _, val = p.parseNumber() + p.expect(')') + break + } + _, re := p.parseNumber() + p.expect('+') + _, im := p.parseNumber() + p.expectKeyword("i") + p.expect(')') + typ = types.Typ[types.UntypedComplex] + val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) + + case scanner.Char: + // rune_lit + typ = types.Typ[types.UntypedRune] + val = constant.MakeFromLiteral(p.lit, token.CHAR, 0) + p.next() + + case scanner.String: + // string_lit + typ = types.Typ[types.UntypedString] + val = constant.MakeFromLiteral(p.lit, token.STRING, 0) + p.next() + + default: + p.errorf("expected literal got %s", scanner.TokenString(p.tok)) + } + + if typ0 == nil { + typ0 = typ + } + + pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val)) +} + +// parseTypeDecl parses a TypeDecl: +// +// TypeDecl = "type" ExportedName Type . +func (p *parser) parseTypeDecl() { + p.expectKeyword("type") + pkg, name := p.parseExportedName() + obj := declTypeName(pkg, name) + + // The type object may have been imported before and thus already + // have a type associated with it. We still need to parse the type + // structure, but throw it away if the object already has a type. + // This ensures that all imports refer to the same type object for + // a given type declaration. + typ := p.parseType(pkg) + + if name := obj.Type().(*types.Named); name.Underlying() == nil { + name.SetUnderlying(typ) + } +} + +// parseVarDecl parses a VarDecl: +// +// VarDecl = "var" ExportedName Type . +func (p *parser) parseVarDecl() { + p.expectKeyword("var") + pkg, name := p.parseExportedName() + typ := p.parseType(pkg) + pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ)) +} + +// parseFunc parses a Func: +// +// Func = Signature [ Body ] . +// Body = "{" ... "}" . +func (p *parser) parseFunc(recv *types.Var) *types.Signature { + sig := p.parseSignature(recv) + if p.tok == '{' { + p.next() + for i := 1; i > 0; p.next() { + switch p.tok { + case '{': + i++ + case '}': + i-- + } + } + } + return sig +} + +// parseMethodDecl parses a MethodDecl: +// +// MethodDecl = "func" Receiver Name Func . +// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . +func (p *parser) parseMethodDecl() { + // "func" already consumed + p.expect('(') + recv, _ := p.parseParameter() // receiver + p.expect(')') + + // determine receiver base type object + base := deref(recv.Type()).(*types.Named) + + // parse method name, signature, and possibly inlined body + _, name := p.parseName(nil, false) + sig := p.parseFunc(recv) + + // methods always belong to the same package as the base type object + pkg := base.Obj().Pkg() + + // add method to type unless type was imported before + // and method exists already + // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small. + base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) +} + +// parseFuncDecl parses a FuncDecl: +// +// FuncDecl = "func" ExportedName Func . +func (p *parser) parseFuncDecl() { + // "func" already consumed + pkg, name := p.parseExportedName() + typ := p.parseFunc(nil) + pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ)) +} + +// parseDecl parses a Decl: +// +// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . +func (p *parser) parseDecl() { + if p.tok == scanner.Ident { + switch p.lit { + case "import": + p.parseImportDecl() + case "const": + p.parseConstDecl() + case "type": + p.parseTypeDecl() + case "var": + p.parseVarDecl() + case "func": + p.next() // look ahead + if p.tok == '(' { + p.parseMethodDecl() + } else { + p.parseFuncDecl() + } + } + } + p.expect('\n') +} + +// ---------------------------------------------------------------------------- +// Export + +// parseExport parses an Export: +// +// Export = "PackageClause { Decl } "$$" . +// PackageClause = "package" PackageName [ "safe" ] "\n" . +func (p *parser) parseExport() *types.Package { + p.expectKeyword("package") + name := p.parsePackageName() + if p.tok == scanner.Ident && p.lit == "safe" { + // package was compiled with -u option - ignore + p.next() + } + p.expect('\n') + + pkg := p.getPkg(p.id, name) + + for p.tok != '$' && p.tok != scanner.EOF { + p.parseDecl() + } + + if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' { + // don't call next()/expect() since reading past the + // export data may cause scanner errors (e.g. NUL chars) + p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch) + } + + if n := p.scanner.ErrorCount; n != 0 { + p.errorf("expected no scanner errors, got %d", n) + } + + // Record all locally referenced packages as imports. + var imports []*types.Package + for id, pkg2 := range p.localPkgs { + if pkg2.Name() == "" { + p.errorf("%s package has no name", id) + } + if id == p.id { + continue // avoid self-edge + } + imports = append(imports, pkg2) + } + sort.Sort(byPath(imports)) + pkg.SetImports(imports) + + // package was imported completely and without errors + pkg.MarkComplete() + + return pkg +} + +type byPath []*types.Package + +func (a byPath) Len() int { return len(a) } +func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() } diff --git a/gopls/golang_org_x_tools/gcimporter/iexport.go b/gopls/golang_org_x_tools/gcimporter/iexport.go new file mode 100644 index 0000000..6532008 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/iexport.go @@ -0,0 +1,1056 @@ +// 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. + +// Indexed binary package export. +// This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; +// see that file for specification of the format. + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "go/types" + "io" + "math/big" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +// IExportShallow encodes "shallow" export data for the specified package. +// +// No promises are made about the encoding other than that it can be +// decoded by the same version of IIExportShallow. If you plan to save +// export data in the file system, be sure to include a cryptographic +// digest of the executable in the key to avoid version skew. +func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) { + // In principle this operation can only fail if out.Write fails, + // but that's impossible for bytes.Buffer---and as a matter of + // fact iexportCommon doesn't even check for I/O errors. + // TODO(adonovan): handle I/O errors properly. + // TODO(adonovan): use byte slices throughout, avoiding copying. + const bundle, shallow = false, true + var out bytes.Buffer + err := iexportCommon(&out, fset, bundle, shallow, iexportVersion, []*types.Package{pkg}) + return out.Bytes(), err +} + +// IImportShallow decodes "shallow" types.Package data encoded by IExportShallow +// in the same executable. This function cannot import data from +// cmd/compile or gcexportdata.Write. +func IImportShallow(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string, insert InsertType) (*types.Package, error) { + const bundle = false + pkgs, err := iimportCommon(fset, imports, data, bundle, path, insert) + if err != nil { + return nil, err + } + return pkgs[0], nil +} + +// InsertType is the type of a function that creates a types.TypeName +// object for a named type and inserts it into the scope of the +// specified Package. +type InsertType = func(pkg *types.Package, name string) + +// Current bundled export format version. Increase with each format change. +// 0: initial implementation +const bundleVersion = 0 + +// IExportData writes indexed export data for pkg to out. +// +// If no file set is provided, position info will be missing. +// The package path of the top-level package will not be recorded, +// so that calls to IImportData can override with a provided package path. +func IExportData(out io.Writer, fset *token.FileSet, pkg *types.Package) error { + const bundle, shallow = false, false + return iexportCommon(out, fset, bundle, shallow, iexportVersion, []*types.Package{pkg}) +} + +// IExportBundle writes an indexed export bundle for pkgs to out. +func IExportBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error { + const bundle, shallow = true, false + return iexportCommon(out, fset, bundle, shallow, iexportVersion, pkgs) +} + +func iexportCommon(out io.Writer, fset *token.FileSet, bundle, shallow bool, version int, pkgs []*types.Package) (err error) { + if !debug { + defer func() { + if e := recover(); e != nil { + if ierr, ok := e.(internalError); ok { + err = ierr + return + } + // Not an internal error; panic again. + panic(e) + } + }() + } + + p := iexporter{ + fset: fset, + version: version, + shallow: shallow, + allPkgs: map[*types.Package]bool{}, + stringIndex: map[string]uint64{}, + declIndex: map[types.Object]uint64{}, + tparamNames: map[types.Object]string{}, + typIndex: map[types.Type]uint64{}, + } + if !bundle { + p.localpkg = pkgs[0] + } + + for i, pt := range predeclared() { + p.typIndex[pt] = uint64(i) + } + if len(p.typIndex) > predeclReserved { + panic(internalErrorf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)) + } + + // Initialize work queue with exported declarations. + for _, pkg := range pkgs { + scope := pkg.Scope() + for _, name := range scope.Names() { + if token.IsExported(name) { + p.pushDecl(scope.Lookup(name)) + } + } + + if bundle { + // Ensure pkg and its imports are included in the index. + p.allPkgs[pkg] = true + for _, imp := range pkg.Imports() { + p.allPkgs[imp] = true + } + } + } + + // Loop until no more work. + for !p.declTodo.empty() { + p.doDecl(p.declTodo.popHead()) + } + + // Append indices to data0 section. + dataLen := uint64(p.data0.Len()) + w := p.newWriter() + w.writeIndex(p.declIndex) + + if bundle { + w.uint64(uint64(len(pkgs))) + for _, pkg := range pkgs { + w.pkg(pkg) + imps := pkg.Imports() + w.uint64(uint64(len(imps))) + for _, imp := range imps { + w.pkg(imp) + } + } + } + w.flush() + + // Assemble header. + var hdr intWriter + if bundle { + hdr.uint64(bundleVersion) + } + hdr.uint64(uint64(p.version)) + hdr.uint64(uint64(p.strings.Len())) + hdr.uint64(dataLen) + + // Flush output. + io.Copy(out, &hdr) + io.Copy(out, &p.strings) + io.Copy(out, &p.data0) + + return nil +} + +// writeIndex writes out an object index. mainIndex indicates whether +// we're writing out the main index, which is also read by +// non-compiler tools and includes a complete package description +// (i.e., name and height). +func (w *exportWriter) writeIndex(index map[types.Object]uint64) { + type pkgObj struct { + obj types.Object + name string // qualified name; differs from obj.Name for type params + } + // Build a map from packages to objects from that package. + pkgObjs := map[*types.Package][]pkgObj{} + + // For the main index, make sure to include every package that + // we reference, even if we're not exporting (or reexporting) + // any symbols from it. + if w.p.localpkg != nil { + pkgObjs[w.p.localpkg] = nil + } + for pkg := range w.p.allPkgs { + pkgObjs[pkg] = nil + } + + for obj := range index { + name := w.p.exportName(obj) + pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], pkgObj{obj, name}) + } + + var pkgs []*types.Package + for pkg, objs := range pkgObjs { + pkgs = append(pkgs, pkg) + + sort.Slice(objs, func(i, j int) bool { + return objs[i].name < objs[j].name + }) + } + + sort.Slice(pkgs, func(i, j int) bool { + return w.exportPath(pkgs[i]) < w.exportPath(pkgs[j]) + }) + + w.uint64(uint64(len(pkgs))) + for _, pkg := range pkgs { + w.string(w.exportPath(pkg)) + w.string(pkg.Name()) + w.uint64(uint64(0)) // package height is not needed for go/types + + objs := pkgObjs[pkg] + w.uint64(uint64(len(objs))) + for _, obj := range objs { + w.string(obj.name) + w.uint64(index[obj.obj]) + } + } +} + +// exportName returns the 'exported' name of an object. It differs from +// obj.Name() only for type parameters (see tparamExportName for details). +func (p *iexporter) exportName(obj types.Object) (res string) { + if name := p.tparamNames[obj]; name != "" { + return name + } + return obj.Name() +} + +type iexporter struct { + fset *token.FileSet + out *bytes.Buffer + version int + + shallow bool // don't put types from other packages in the index + localpkg *types.Package // (nil in bundle mode) + + // allPkgs tracks all packages that have been referenced by + // the export data, so we can ensure to include them in the + // main index. + allPkgs map[*types.Package]bool + + declTodo objQueue + + strings intWriter + stringIndex map[string]uint64 + + data0 intWriter + declIndex map[types.Object]uint64 + tparamNames map[types.Object]string // typeparam->exported name + typIndex map[types.Type]uint64 + + indent int // for tracing support +} + +func (p *iexporter) trace(format string, args ...interface{}) { + if !trace { + // Call sites should also be guarded, but having this check here allows + // easily enabling/disabling debug trace statements. + return + } + fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) +} + +// stringOff returns the offset of s within the string section. +// If not already present, it's added to the end. +func (p *iexporter) stringOff(s string) uint64 { + off, ok := p.stringIndex[s] + if !ok { + off = uint64(p.strings.Len()) + p.stringIndex[s] = off + + p.strings.uint64(uint64(len(s))) + p.strings.WriteString(s) + } + return off +} + +// pushDecl adds n to the declaration work queue, if not already present. +func (p *iexporter) pushDecl(obj types.Object) { + // Package unsafe is known to the compiler and predeclared. + // Caller should not ask us to do export it. + if obj.Pkg() == types.Unsafe { + panic("cannot export package unsafe") + } + + // Shallow export data: don't index decls from other packages. + if p.shallow && obj.Pkg() != p.localpkg { + return + } + + if _, ok := p.declIndex[obj]; ok { + return + } + + p.declIndex[obj] = ^uint64(0) // mark obj present in work queue + p.declTodo.pushTail(obj) +} + +// exportWriter handles writing out individual data section chunks. +type exportWriter struct { + p *iexporter + + data intWriter + currPkg *types.Package + prevFile string + prevLine int64 + prevColumn int64 +} + +func (w *exportWriter) exportPath(pkg *types.Package) string { + if pkg == w.p.localpkg { + return "" + } + return pkg.Path() +} + +func (p *iexporter) doDecl(obj types.Object) { + if trace { + p.trace("exporting decl %v (%T)", obj, obj) + p.indent++ + defer func() { + p.indent-- + p.trace("=> %s", obj) + }() + } + w := p.newWriter() + w.setPkg(obj.Pkg(), false) + + switch obj := obj.(type) { + case *types.Var: + w.tag('V') + w.pos(obj.Pos()) + w.typ(obj.Type(), obj.Pkg()) + + case *types.Func: + sig, _ := obj.Type().(*types.Signature) + if sig.Recv() != nil { + panic(internalErrorf("unexpected method: %v", sig)) + } + + // Function. + if typeparams.ForSignature(sig).Len() == 0 { + w.tag('F') + } else { + w.tag('G') + } + w.pos(obj.Pos()) + // The tparam list of the function type is the declaration of the type + // params. So, write out the type params right now. Then those type params + // will be referenced via their type offset (via typOff) in all other + // places in the signature and function where they are used. + // + // While importing the type parameters, tparamList computes and records + // their export name, so that it can be later used when writing the index. + if tparams := typeparams.ForSignature(sig); tparams.Len() > 0 { + w.tparamList(obj.Name(), tparams, obj.Pkg()) + } + w.signature(sig) + + case *types.Const: + w.tag('C') + w.pos(obj.Pos()) + w.value(obj.Type(), obj.Val()) + + case *types.TypeName: + t := obj.Type() + + if tparam, ok := t.(*typeparams.TypeParam); ok { + w.tag('P') + w.pos(obj.Pos()) + constraint := tparam.Constraint() + if p.version >= iexportVersionGo1_18 { + implicit := false + if iface, _ := constraint.(*types.Interface); iface != nil { + implicit = typeparams.IsImplicit(iface) + } + w.bool(implicit) + } + w.typ(constraint, obj.Pkg()) + break + } + + if obj.IsAlias() { + w.tag('A') + w.pos(obj.Pos()) + w.typ(t, obj.Pkg()) + break + } + + // Defined type. + named, ok := t.(*types.Named) + if !ok { + panic(internalErrorf("%s is not a defined type", t)) + } + + if typeparams.ForNamed(named).Len() == 0 { + w.tag('T') + } else { + w.tag('U') + } + w.pos(obj.Pos()) + + if typeparams.ForNamed(named).Len() > 0 { + // While importing the type parameters, tparamList computes and records + // their export name, so that it can be later used when writing the index. + w.tparamList(obj.Name(), typeparams.ForNamed(named), obj.Pkg()) + } + + underlying := obj.Type().Underlying() + w.typ(underlying, obj.Pkg()) + + if types.IsInterface(t) { + break + } + + n := named.NumMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := named.Method(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + + // Receiver type parameters are type arguments of the receiver type, so + // their name must be qualified before exporting recv. + if rparams := typeparams.RecvTypeParams(sig); rparams.Len() > 0 { + prefix := obj.Name() + "." + m.Name() + for i := 0; i < rparams.Len(); i++ { + rparam := rparams.At(i) + name := tparamExportName(prefix, rparam) + w.p.tparamNames[rparam.Obj()] = name + } + } + w.param(sig.Recv()) + w.signature(sig) + } + + default: + panic(internalErrorf("unexpected object: %v", obj)) + } + + p.declIndex[obj] = w.flush() +} + +func (w *exportWriter) tag(tag byte) { + w.data.WriteByte(tag) +} + +func (w *exportWriter) pos(pos token.Pos) { + if w.p.version >= iexportVersionPosCol { + w.posV1(pos) + } else { + w.posV0(pos) + } +} + +func (w *exportWriter) posV1(pos token.Pos) { + if w.p.fset == nil { + w.int64(0) + return + } + + p := w.p.fset.Position(pos) + file := p.Filename + line := int64(p.Line) + column := int64(p.Column) + + deltaColumn := (column - w.prevColumn) << 1 + deltaLine := (line - w.prevLine) << 1 + + if file != w.prevFile { + deltaLine |= 1 + } + if deltaLine != 0 { + deltaColumn |= 1 + } + + w.int64(deltaColumn) + if deltaColumn&1 != 0 { + w.int64(deltaLine) + if deltaLine&1 != 0 { + w.string(file) + } + } + + w.prevFile = file + w.prevLine = line + w.prevColumn = column +} + +func (w *exportWriter) posV0(pos token.Pos) { + if w.p.fset == nil { + w.int64(0) + return + } + + p := w.p.fset.Position(pos) + file := p.Filename + line := int64(p.Line) + + // When file is the same as the last position (common case), + // we can save a few bytes by delta encoding just the line + // number. + // + // Note: Because data objects may be read out of order (or not + // at all), we can only apply delta encoding within a single + // object. This is handled implicitly by tracking prevFile and + // prevLine as fields of exportWriter. + + if file == w.prevFile { + delta := line - w.prevLine + w.int64(delta) + if delta == deltaNewFile { + w.int64(-1) + } + } else { + w.int64(deltaNewFile) + w.int64(line) // line >= 0 + w.string(file) + w.prevFile = file + } + w.prevLine = line +} + +func (w *exportWriter) pkg(pkg *types.Package) { + // Ensure any referenced packages are declared in the main index. + w.p.allPkgs[pkg] = true + + w.string(w.exportPath(pkg)) +} + +func (w *exportWriter) qualifiedType(obj *types.TypeName) { + name := w.p.exportName(obj) + + // Ensure any referenced declarations are written out too. + w.p.pushDecl(obj) + w.string(name) + w.pkg(obj.Pkg()) +} + +func (w *exportWriter) typ(t types.Type, pkg *types.Package) { + w.data.uint64(w.p.typOff(t, pkg)) +} + +func (p *iexporter) newWriter() *exportWriter { + return &exportWriter{p: p} +} + +func (w *exportWriter) flush() uint64 { + off := uint64(w.p.data0.Len()) + io.Copy(&w.p.data0, &w.data) + return off +} + +func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 { + off, ok := p.typIndex[t] + if !ok { + w := p.newWriter() + w.doTyp(t, pkg) + off = predeclReserved + w.flush() + p.typIndex[t] = off + } + return off +} + +func (w *exportWriter) startType(k itag) { + w.data.uint64(uint64(k)) +} + +func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { + if trace { + w.p.trace("exporting type %s (%T)", t, t) + w.p.indent++ + defer func() { + w.p.indent-- + w.p.trace("=> %s", t) + }() + } + switch t := t.(type) { + case *types.Named: + if targs := typeparams.NamedTypeArgs(t); targs.Len() > 0 { + w.startType(instanceType) + // TODO(rfindley): investigate if this position is correct, and if it + // matters. + w.pos(t.Obj().Pos()) + w.typeList(targs, pkg) + w.typ(typeparams.NamedTypeOrigin(t), pkg) + return + } + w.startType(definedType) + w.qualifiedType(t.Obj()) + + case *typeparams.TypeParam: + w.startType(typeParamType) + w.qualifiedType(t.Obj()) + + case *types.Pointer: + w.startType(pointerType) + w.typ(t.Elem(), pkg) + + case *types.Slice: + w.startType(sliceType) + w.typ(t.Elem(), pkg) + + case *types.Array: + w.startType(arrayType) + w.uint64(uint64(t.Len())) + w.typ(t.Elem(), pkg) + + case *types.Chan: + w.startType(chanType) + // 1 RecvOnly; 2 SendOnly; 3 SendRecv + var dir uint64 + switch t.Dir() { + case types.RecvOnly: + dir = 1 + case types.SendOnly: + dir = 2 + case types.SendRecv: + dir = 3 + } + w.uint64(dir) + w.typ(t.Elem(), pkg) + + case *types.Map: + w.startType(mapType) + w.typ(t.Key(), pkg) + w.typ(t.Elem(), pkg) + + case *types.Signature: + w.startType(signatureType) + w.setPkg(pkg, true) + w.signature(t) + + case *types.Struct: + w.startType(structType) + n := t.NumFields() + if n > 0 { + w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects + } else { + w.setPkg(pkg, true) + } + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + f := t.Field(i) + w.pos(f.Pos()) + w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg + w.typ(f.Type(), pkg) + w.bool(f.Anonymous()) + w.string(t.Tag(i)) // note (or tag) + } + + case *types.Interface: + w.startType(interfaceType) + w.setPkg(pkg, true) + + n := t.NumEmbeddeds() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + ft := t.EmbeddedType(i) + tPkg := pkg + if named, _ := ft.(*types.Named); named != nil { + w.pos(named.Obj().Pos()) + } else { + w.pos(token.NoPos) + } + w.typ(ft, tPkg) + } + + n = t.NumExplicitMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := t.ExplicitMethod(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + w.signature(sig) + } + + case *typeparams.Union: + w.startType(unionType) + nt := t.Len() + w.uint64(uint64(nt)) + for i := 0; i < nt; i++ { + term := t.Term(i) + w.bool(term.Tilde()) + w.typ(term.Type(), pkg) + } + + default: + panic(internalErrorf("unexpected type: %v, %v", t, reflect.TypeOf(t))) + } +} + +func (w *exportWriter) setPkg(pkg *types.Package, write bool) { + if write { + w.pkg(pkg) + } + + w.currPkg = pkg +} + +func (w *exportWriter) signature(sig *types.Signature) { + w.paramList(sig.Params()) + w.paramList(sig.Results()) + if sig.Params().Len() > 0 { + w.bool(sig.Variadic()) + } +} + +func (w *exportWriter) typeList(ts *typeparams.TypeList, pkg *types.Package) { + w.uint64(uint64(ts.Len())) + for i := 0; i < ts.Len(); i++ { + w.typ(ts.At(i), pkg) + } +} + +func (w *exportWriter) tparamList(prefix string, list *typeparams.TypeParamList, pkg *types.Package) { + ll := uint64(list.Len()) + w.uint64(ll) + for i := 0; i < list.Len(); i++ { + tparam := list.At(i) + // Set the type parameter exportName before exporting its type. + exportName := tparamExportName(prefix, tparam) + w.p.tparamNames[tparam.Obj()] = exportName + w.typ(list.At(i), pkg) + } +} + +const blankMarker = "$" + +// tparamExportName returns the 'exported' name of a type parameter, which +// differs from its actual object name: it is prefixed with a qualifier, and +// blank type parameter names are disambiguated by their index in the type +// parameter list. +func tparamExportName(prefix string, tparam *typeparams.TypeParam) string { + assert(prefix != "") + name := tparam.Obj().Name() + if name == "_" { + name = blankMarker + strconv.Itoa(tparam.Index()) + } + return prefix + "." + name +} + +// tparamName returns the real name of a type parameter, after stripping its +// qualifying prefix and reverting blank-name encoding. See tparamExportName +// for details. +func tparamName(exportName string) string { + // Remove the "path" from the type param name that makes it unique. + ix := strings.LastIndex(exportName, ".") + if ix < 0 { + errorf("malformed type parameter export name %s: missing prefix", exportName) + } + name := exportName[ix+1:] + if strings.HasPrefix(name, blankMarker) { + return "_" + } + return name +} + +func (w *exportWriter) paramList(tup *types.Tuple) { + n := tup.Len() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + w.param(tup.At(i)) + } +} + +func (w *exportWriter) param(obj types.Object) { + w.pos(obj.Pos()) + w.localIdent(obj) + w.typ(obj.Type(), obj.Pkg()) +} + +func (w *exportWriter) value(typ types.Type, v constant.Value) { + w.typ(typ, nil) + if w.p.version >= iexportVersionGo1_18 { + w.int64(int64(v.Kind())) + } + + switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { + case types.IsBoolean: + w.bool(constant.BoolVal(v)) + case types.IsInteger: + var i big.Int + if i64, exact := constant.Int64Val(v); exact { + i.SetInt64(i64) + } else if ui64, exact := constant.Uint64Val(v); exact { + i.SetUint64(ui64) + } else { + i.SetString(v.ExactString(), 10) + } + w.mpint(&i, typ) + case types.IsFloat: + f := constantToFloat(v) + w.mpfloat(f, typ) + case types.IsComplex: + w.mpfloat(constantToFloat(constant.Real(v)), typ) + w.mpfloat(constantToFloat(constant.Imag(v)), typ) + case types.IsString: + w.string(constant.StringVal(v)) + default: + if b.Kind() == types.Invalid { + // package contains type errors + break + } + panic(internalErrorf("unexpected type %v (%v)", typ, typ.Underlying())) + } +} + +// constantToFloat converts a constant.Value with kind constant.Float to a +// big.Float. +func constantToFloat(x constant.Value) *big.Float { + x = constant.ToFloat(x) + // Use the same floating-point precision (512) as cmd/compile + // (see Mpprec in cmd/compile/internal/gc/mpfloat.go). + const mpprec = 512 + var f big.Float + f.SetPrec(mpprec) + if v, exact := constant.Float64Val(x); exact { + // float64 + f.SetFloat64(v) + } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { + // TODO(gri): add big.Rat accessor to constant.Value. + n := valueToRat(num) + d := valueToRat(denom) + f.SetRat(n.Quo(n, d)) + } else { + // Value too large to represent as a fraction => inaccessible. + // TODO(gri): add big.Float accessor to constant.Value. + _, ok := f.SetString(x.ExactString()) + assert(ok) + } + return &f +} + +// mpint exports a multi-precision integer. +// +// For unsigned types, small values are written out as a single +// byte. Larger values are written out as a length-prefixed big-endian +// byte string, where the length prefix is encoded as its complement. +// For example, bytes 0, 1, and 2 directly represent the integer +// values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-, +// 2-, and 3-byte big-endian string follow. +// +// Encoding for signed types use the same general approach as for +// unsigned types, except small values use zig-zag encoding and the +// bottom bit of length prefix byte for large values is reserved as a +// sign bit. +// +// The exact boundary between small and large encodings varies +// according to the maximum number of bytes needed to encode a value +// of type typ. As a special case, 8-bit types are always encoded as a +// single byte. +// +// TODO(mdempsky): Is this level of complexity really worthwhile? +func (w *exportWriter) mpint(x *big.Int, typ types.Type) { + basic, ok := typ.Underlying().(*types.Basic) + if !ok { + panic(internalErrorf("unexpected type %v (%T)", typ.Underlying(), typ.Underlying())) + } + + signed, maxBytes := intSize(basic) + + negative := x.Sign() < 0 + if !signed && negative { + panic(internalErrorf("negative unsigned integer; type %v, value %v", typ, x)) + } + + b := x.Bytes() + if len(b) > 0 && b[0] == 0 { + panic(internalErrorf("leading zeros")) + } + if uint(len(b)) > maxBytes { + panic(internalErrorf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)) + } + + maxSmall := 256 - maxBytes + if signed { + maxSmall = 256 - 2*maxBytes + } + if maxBytes == 1 { + maxSmall = 256 + } + + // Check if x can use small value encoding. + if len(b) <= 1 { + var ux uint + if len(b) == 1 { + ux = uint(b[0]) + } + if signed { + ux <<= 1 + if negative { + ux-- + } + } + if ux < maxSmall { + w.data.WriteByte(byte(ux)) + return + } + } + + n := 256 - uint(len(b)) + if signed { + n = 256 - 2*uint(len(b)) + if negative { + n |= 1 + } + } + if n < maxSmall || n >= 256 { + panic(internalErrorf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)) + } + + w.data.WriteByte(byte(n)) + w.data.Write(b) +} + +// mpfloat exports a multi-precision floating point number. +// +// The number's value is decomposed into mantissa × 2**exponent, where +// mantissa is an integer. The value is written out as mantissa (as a +// multi-precision integer) and then the exponent, except exponent is +// omitted if mantissa is zero. +func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) { + if f.IsInf() { + panic("infinite constant") + } + + // Break into f = mant × 2**exp, with 0.5 <= mant < 1. + var mant big.Float + exp := int64(f.MantExp(&mant)) + + // Scale so that mant is an integer. + prec := mant.MinPrec() + mant.SetMantExp(&mant, int(prec)) + exp -= int64(prec) + + manti, acc := mant.Int(nil) + if acc != big.Exact { + panic(internalErrorf("mantissa scaling failed for %f (%s)", f, acc)) + } + w.mpint(manti, typ) + if manti.Sign() != 0 { + w.int64(exp) + } +} + +func (w *exportWriter) bool(b bool) bool { + var x uint64 + if b { + x = 1 + } + w.uint64(x) + return b +} + +func (w *exportWriter) int64(x int64) { w.data.int64(x) } +func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) } +func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) } + +func (w *exportWriter) localIdent(obj types.Object) { + // Anonymous parameters. + if obj == nil { + w.string("") + return + } + + name := obj.Name() + if name == "_" { + w.string("_") + return + } + + w.string(name) +} + +type intWriter struct { + bytes.Buffer +} + +func (w *intWriter) int64(x int64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutVarint(buf[:], x) + w.Write(buf[:n]) +} + +func (w *intWriter) uint64(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + w.Write(buf[:n]) +} + +func assert(cond bool) { + if !cond { + panic("internal error: assertion failed") + } +} + +// The below is copied from go/src/cmd/compile/internal/gc/syntax.go. + +// objQueue is a FIFO queue of types.Object. The zero value of objQueue is +// a ready-to-use empty queue. +type objQueue struct { + ring []types.Object + head, tail int +} + +// empty returns true if q contains no Nodes. +func (q *objQueue) empty() bool { + return q.head == q.tail +} + +// pushTail appends n to the tail of the queue. +func (q *objQueue) pushTail(obj types.Object) { + if len(q.ring) == 0 { + q.ring = make([]types.Object, 16) + } else if q.head+len(q.ring) == q.tail { + // Grow the ring. + nring := make([]types.Object, len(q.ring)*2) + // Copy the old elements. + part := q.ring[q.head%len(q.ring):] + if q.tail-q.head <= len(part) { + part = part[:q.tail-q.head] + copy(nring, part) + } else { + pos := copy(nring, part) + copy(nring[pos:], q.ring[:q.tail%len(q.ring)]) + } + q.ring, q.head, q.tail = nring, 0, q.tail-q.head + } + + q.ring[q.tail%len(q.ring)] = obj + q.tail++ +} + +// popHead pops a node from the head of the queue. It panics if q is empty. +func (q *objQueue) popHead() types.Object { + if q.empty() { + panic("dequeue empty") + } + obj := q.ring[q.head%len(q.ring)] + q.head++ + return obj +} diff --git a/gopls/golang_org_x_tools/gcimporter/iimport.go b/gopls/golang_org_x_tools/gcimporter/iimport.go new file mode 100644 index 0000000..c29dcf4 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/iimport.go @@ -0,0 +1,898 @@ +// 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. + +// Indexed package import. +// See cmd/compile/internal/gc/iexport.go for the export data format. + +// This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go. + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "go/types" + "io" + "math/big" + "sort" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +type intReader struct { + *bytes.Reader + path string +} + +func (r *intReader) int64() int64 { + i, err := binary.ReadVarint(r.Reader) + if err != nil { + errorf("import %q: read varint error: %v", r.path, err) + } + return i +} + +func (r *intReader) uint64() uint64 { + i, err := binary.ReadUvarint(r.Reader) + if err != nil { + errorf("import %q: read varint error: %v", r.path, err) + } + return i +} + +// Keep this in sync with constants in iexport.go. +const ( + iexportVersionGo1_11 = 0 + iexportVersionPosCol = 1 + iexportVersionGo1_18 = 2 + iexportVersionGenerics = 2 + + iexportVersionCurrent = 2 +) + +type ident struct { + pkg *types.Package + name string +} + +const predeclReserved = 32 + +type itag uint64 + +const ( + // Types + definedType itag = iota + pointerType + sliceType + arrayType + chanType + mapType + signatureType + structType + interfaceType + typeParamType + instanceType + unionType +) + +// IImportData imports a package from the serialized package data +// and returns 0 and a reference to the package. +// If the export data version is not recognized or the format is otherwise +// compromised, an error is returned. +func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { + pkgs, err := iimportCommon(fset, imports, data, false, path, nil) + if err != nil { + return 0, nil, err + } + return 0, pkgs[0], nil +} + +// IImportBundle imports a set of packages from the serialized package bundle. +func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) { + return iimportCommon(fset, imports, data, true, "", nil) +} + +func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data []byte, bundle bool, path string, insert InsertType) (pkgs []*types.Package, err error) { + const currentVersion = iexportVersionCurrent + version := int64(-1) + if !debug { + defer func() { + if e := recover(); e != nil { + if bundle { + err = fmt.Errorf("%v", e) + } else if version > currentVersion { + err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) + } else { + err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + } + } + }() + } + + r := &intReader{bytes.NewReader(data), path} + + if bundle { + bundleVersion := r.uint64() + switch bundleVersion { + case bundleVersion: + default: + errorf("unknown bundle format version %d", bundleVersion) + } + } + + version = int64(r.uint64()) + switch version { + case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11: + default: + if version > iexportVersionGo1_18 { + errorf("unstable iexport format version %d, just rebuild compiler and std library", version) + } else { + errorf("unknown iexport format version %d", version) + } + } + + sLen := int64(r.uint64()) + dLen := int64(r.uint64()) + + whence, _ := r.Seek(0, io.SeekCurrent) + stringData := data[whence : whence+sLen] + declData := data[whence+sLen : whence+sLen+dLen] + r.Seek(sLen+dLen, io.SeekCurrent) + + p := iimporter{ + version: int(version), + ipath: path, + insert: insert, + + stringData: stringData, + stringCache: make(map[uint64]string), + pkgCache: make(map[uint64]*types.Package), + + declData: declData, + pkgIndex: make(map[*types.Package]map[string]uint64), + typCache: make(map[uint64]types.Type), + // Separate map for typeparams, keyed by their package and unique + // name. + tparamIndex: make(map[ident]types.Type), + + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*fileInfo), + }, + } + defer p.fake.setLines() // set lines for files in fset + + for i, pt := range predeclared() { + p.typCache[uint64(i)] = pt + } + + pkgList := make([]*types.Package, r.uint64()) + for i := range pkgList { + pkgPathOff := r.uint64() + pkgPath := p.stringAt(pkgPathOff) + pkgName := p.stringAt(r.uint64()) + _ = r.uint64() // package height; unused by go/types + + if pkgPath == "" { + pkgPath = path + } + pkg := imports[pkgPath] + if pkg == nil { + pkg = types.NewPackage(pkgPath, pkgName) + imports[pkgPath] = pkg + } else if pkg.Name() != pkgName { + errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) + } + if i == 0 && !bundle { + p.localpkg = pkg + } + + p.pkgCache[pkgPathOff] = pkg + + // Read index for package. + nameIndex := make(map[string]uint64) + nSyms := r.uint64() + // In shallow mode we don't expect an index for other packages. + assert(nSyms == 0 || p.localpkg == pkg || p.insert == nil) + for ; nSyms > 0; nSyms-- { + name := p.stringAt(r.uint64()) + nameIndex[name] = r.uint64() + } + + p.pkgIndex[pkg] = nameIndex + pkgList[i] = pkg + } + + if bundle { + pkgs = make([]*types.Package, r.uint64()) + for i := range pkgs { + pkg := p.pkgAt(r.uint64()) + imps := make([]*types.Package, r.uint64()) + for j := range imps { + imps[j] = p.pkgAt(r.uint64()) + } + pkg.SetImports(imps) + pkgs[i] = pkg + } + } else { + if len(pkgList) == 0 { + errorf("no packages found for %s", path) + panic("unreachable") + } + pkgs = pkgList[:1] + + // record all referenced packages as imports + list := append(([]*types.Package)(nil), pkgList[1:]...) + sort.Sort(byPath(list)) + pkgs[0].SetImports(list) + } + + for _, pkg := range pkgs { + if pkg.Complete() { + continue + } + + names := make([]string, 0, len(p.pkgIndex[pkg])) + for name := range p.pkgIndex[pkg] { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + p.doDecl(pkg, name) + } + + // package was imported completely and without errors + pkg.MarkComplete() + } + + // SetConstraint can't be called if the constraint type is not yet complete. + // When type params are created in the 'P' case of (*importReader).obj(), + // the associated constraint type may not be complete due to recursion. + // Therefore, we defer calling SetConstraint there, and call it here instead + // after all types are complete. + for _, d := range p.later { + typeparams.SetTypeParamConstraint(d.t, d.constraint) + } + + for _, typ := range p.interfaceList { + typ.Complete() + } + + return pkgs, nil +} + +type setConstraintArgs struct { + t *typeparams.TypeParam + constraint types.Type +} + +type iimporter struct { + version int + ipath string + + localpkg *types.Package + insert func(pkg *types.Package, name string) // "shallow" mode only + + stringData []byte + stringCache map[uint64]string + pkgCache map[uint64]*types.Package + + declData []byte + pkgIndex map[*types.Package]map[string]uint64 + typCache map[uint64]types.Type + tparamIndex map[ident]types.Type + + fake fakeFileSet + interfaceList []*types.Interface + + // Arguments for calls to SetConstraint that are deferred due to recursive types + later []setConstraintArgs + + indent int // for tracing support +} + +func (p *iimporter) trace(format string, args ...interface{}) { + if !trace { + // Call sites should also be guarded, but having this check here allows + // easily enabling/disabling debug trace statements. + return + } + fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) +} + +func (p *iimporter) doDecl(pkg *types.Package, name string) { + if debug { + p.trace("import decl %s", name) + p.indent++ + defer func() { + p.indent-- + p.trace("=> %s", name) + }() + } + // See if we've already imported this declaration. + if obj := pkg.Scope().Lookup(name); obj != nil { + return + } + + off, ok := p.pkgIndex[pkg][name] + if !ok { + // In "shallow" mode, call back to the application to + // find the object and insert it into the package scope. + if p.insert != nil { + assert(pkg != p.localpkg) + p.insert(pkg, name) // "can't fail" + return + } + errorf("%v.%v not in index", pkg, name) + } + + r := &importReader{p: p, currPkg: pkg} + r.declReader.Reset(p.declData[off:]) + + r.obj(name) +} + +func (p *iimporter) stringAt(off uint64) string { + if s, ok := p.stringCache[off]; ok { + return s + } + + slen, n := binary.Uvarint(p.stringData[off:]) + if n <= 0 { + errorf("varint failed") + } + spos := off + uint64(n) + s := string(p.stringData[spos : spos+slen]) + p.stringCache[off] = s + return s +} + +func (p *iimporter) pkgAt(off uint64) *types.Package { + if pkg, ok := p.pkgCache[off]; ok { + return pkg + } + path := p.stringAt(off) + errorf("missing package %q in %q", path, p.ipath) + return nil +} + +func (p *iimporter) typAt(off uint64, base *types.Named) types.Type { + if t, ok := p.typCache[off]; ok && canReuse(base, t) { + return t + } + + if off < predeclReserved { + errorf("predeclared type missing from cache: %v", off) + } + + r := &importReader{p: p} + r.declReader.Reset(p.declData[off-predeclReserved:]) + t := r.doType(base) + + if canReuse(base, t) { + p.typCache[off] = t + } + return t +} + +// canReuse reports whether the type rhs on the RHS of the declaration for def +// may be re-used. +// +// Specifically, if def is non-nil and rhs is an interface type with methods, it +// may not be re-used because we have a convention of setting the receiver type +// for interface methods to def. +func canReuse(def *types.Named, rhs types.Type) bool { + if def == nil { + return true + } + iface, _ := rhs.(*types.Interface) + if iface == nil { + return true + } + // Don't use iface.Empty() here as iface may not be complete. + return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0 +} + +type importReader struct { + p *iimporter + declReader bytes.Reader + currPkg *types.Package + prevFile string + prevLine int64 + prevColumn int64 +} + +func (r *importReader) obj(name string) { + tag := r.byte() + pos := r.pos() + + switch tag { + case 'A': + typ := r.typ() + + r.declare(types.NewTypeName(pos, r.currPkg, name, typ)) + + case 'C': + typ, val := r.value() + + r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) + + case 'F', 'G': + var tparams []*typeparams.TypeParam + if tag == 'G' { + tparams = r.tparamList() + } + sig := r.signature(nil, nil, tparams) + r.declare(types.NewFunc(pos, r.currPkg, name, sig)) + + case 'T', 'U': + // Types can be recursive. We need to setup a stub + // declaration before recursing. + obj := types.NewTypeName(pos, r.currPkg, name, nil) + named := types.NewNamed(obj, nil, nil) + // Declare obj before calling r.tparamList, so the new type name is recognized + // if used in the constraint of one of its own typeparams (see #48280). + r.declare(obj) + if tag == 'U' { + tparams := r.tparamList() + typeparams.SetForNamed(named, tparams) + } + + underlying := r.p.typAt(r.uint64(), named).Underlying() + named.SetUnderlying(underlying) + + if !isInterface(underlying) { + for n := r.uint64(); n > 0; n-- { + mpos := r.pos() + mname := r.ident() + recv := r.param() + + // If the receiver has any targs, set those as the + // rparams of the method (since those are the + // typeparams being used in the method sig/body). + base := baseType(recv.Type()) + assert(base != nil) + targs := typeparams.NamedTypeArgs(base) + var rparams []*typeparams.TypeParam + if targs.Len() > 0 { + rparams = make([]*typeparams.TypeParam, targs.Len()) + for i := range rparams { + rparams[i] = targs.At(i).(*typeparams.TypeParam) + } + } + msig := r.signature(recv, rparams, nil) + + named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) + } + } + + case 'P': + // We need to "declare" a typeparam in order to have a name that + // can be referenced recursively (if needed) in the type param's + // bound. + if r.p.version < iexportVersionGenerics { + errorf("unexpected type param type") + } + name0 := tparamName(name) + tn := types.NewTypeName(pos, r.currPkg, name0, nil) + t := typeparams.NewTypeParam(tn, nil) + + // To handle recursive references to the typeparam within its + // bound, save the partial type in tparamIndex before reading the bounds. + id := ident{r.currPkg, name} + r.p.tparamIndex[id] = t + var implicit bool + if r.p.version >= iexportVersionGo1_18 { + implicit = r.bool() + } + constraint := r.typ() + if implicit { + iface, _ := constraint.(*types.Interface) + if iface == nil { + errorf("non-interface constraint marked implicit") + } + typeparams.MarkImplicit(iface) + } + // The constraint type may not be complete, if we + // are in the middle of a type recursion involving type + // constraints. So, we defer SetConstraint until we have + // completely set up all types in ImportData. + r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint}) + + case 'V': + typ := r.typ() + + r.declare(types.NewVar(pos, r.currPkg, name, typ)) + + default: + errorf("unexpected tag: %v", tag) + } +} + +func (r *importReader) declare(obj types.Object) { + obj.Pkg().Scope().Insert(obj) +} + +func (r *importReader) value() (typ types.Type, val constant.Value) { + typ = r.typ() + if r.p.version >= iexportVersionGo1_18 { + // TODO: add support for using the kind. + _ = constant.Kind(r.int64()) + } + + switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { + case types.IsBoolean: + val = constant.MakeBool(r.bool()) + + case types.IsString: + val = constant.MakeString(r.string()) + + case types.IsInteger: + var x big.Int + r.mpint(&x, b) + val = constant.Make(&x) + + case types.IsFloat: + val = r.mpfloat(b) + + case types.IsComplex: + re := r.mpfloat(b) + im := r.mpfloat(b) + val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) + + default: + if b.Kind() == types.Invalid { + val = constant.MakeUnknown() + return + } + errorf("unexpected type %v", typ) // panics + panic("unreachable") + } + + return +} + +func intSize(b *types.Basic) (signed bool, maxBytes uint) { + if (b.Info() & types.IsUntyped) != 0 { + return true, 64 + } + + switch b.Kind() { + case types.Float32, types.Complex64: + return true, 3 + case types.Float64, types.Complex128: + return true, 7 + } + + signed = (b.Info() & types.IsUnsigned) == 0 + switch b.Kind() { + case types.Int8, types.Uint8: + maxBytes = 1 + case types.Int16, types.Uint16: + maxBytes = 2 + case types.Int32, types.Uint32: + maxBytes = 4 + default: + maxBytes = 8 + } + + return +} + +func (r *importReader) mpint(x *big.Int, typ *types.Basic) { + signed, maxBytes := intSize(typ) + + maxSmall := 256 - maxBytes + if signed { + maxSmall = 256 - 2*maxBytes + } + if maxBytes == 1 { + maxSmall = 256 + } + + n, _ := r.declReader.ReadByte() + if uint(n) < maxSmall { + v := int64(n) + if signed { + v >>= 1 + if n&1 != 0 { + v = ^v + } + } + x.SetInt64(v) + return + } + + v := -n + if signed { + v = -(n &^ 1) >> 1 + } + if v < 1 || uint(v) > maxBytes { + errorf("weird decoding: %v, %v => %v", n, signed, v) + } + b := make([]byte, v) + io.ReadFull(&r.declReader, b) + x.SetBytes(b) + if signed && n&1 != 0 { + x.Neg(x) + } +} + +func (r *importReader) mpfloat(typ *types.Basic) constant.Value { + var mant big.Int + r.mpint(&mant, typ) + var f big.Float + f.SetInt(&mant) + if f.Sign() != 0 { + f.SetMantExp(&f, int(r.int64())) + } + return constant.Make(&f) +} + +func (r *importReader) ident() string { + return r.string() +} + +func (r *importReader) qualifiedIdent() (*types.Package, string) { + name := r.string() + pkg := r.pkg() + return pkg, name +} + +func (r *importReader) pos() token.Pos { + if r.p.version >= iexportVersionPosCol { + r.posv1() + } else { + r.posv0() + } + + if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 { + return token.NoPos + } + return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn)) +} + +func (r *importReader) posv0() { + delta := r.int64() + if delta != deltaNewFile { + r.prevLine += delta + } else if l := r.int64(); l == -1 { + r.prevLine += deltaNewFile + } else { + r.prevFile = r.string() + r.prevLine = l + } +} + +func (r *importReader) posv1() { + delta := r.int64() + r.prevColumn += delta >> 1 + if delta&1 != 0 { + delta = r.int64() + r.prevLine += delta >> 1 + if delta&1 != 0 { + r.prevFile = r.string() + } + } +} + +func (r *importReader) typ() types.Type { + return r.p.typAt(r.uint64(), nil) +} + +func isInterface(t types.Type) bool { + _, ok := t.(*types.Interface) + return ok +} + +func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) } +func (r *importReader) string() string { return r.p.stringAt(r.uint64()) } + +func (r *importReader) doType(base *types.Named) (res types.Type) { + k := r.kind() + if debug { + r.p.trace("importing type %d (base: %s)", k, base) + r.p.indent++ + defer func() { + r.p.indent-- + r.p.trace("=> %s", res) + }() + } + switch k { + default: + errorf("unexpected kind tag in %q: %v", r.p.ipath, k) + return nil + + case definedType: + pkg, name := r.qualifiedIdent() + r.p.doDecl(pkg, name) + return pkg.Scope().Lookup(name).(*types.TypeName).Type() + case pointerType: + return types.NewPointer(r.typ()) + case sliceType: + return types.NewSlice(r.typ()) + case arrayType: + n := r.uint64() + return types.NewArray(r.typ(), int64(n)) + case chanType: + dir := chanDir(int(r.uint64())) + return types.NewChan(dir, r.typ()) + case mapType: + return types.NewMap(r.typ(), r.typ()) + case signatureType: + r.currPkg = r.pkg() + return r.signature(nil, nil, nil) + + case structType: + r.currPkg = r.pkg() + + fields := make([]*types.Var, r.uint64()) + tags := make([]string, len(fields)) + for i := range fields { + fpos := r.pos() + fname := r.ident() + ftyp := r.typ() + emb := r.bool() + tag := r.string() + + fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) + tags[i] = tag + } + return types.NewStruct(fields, tags) + + case interfaceType: + r.currPkg = r.pkg() + + embeddeds := make([]types.Type, r.uint64()) + for i := range embeddeds { + _ = r.pos() + embeddeds[i] = r.typ() + } + + methods := make([]*types.Func, r.uint64()) + for i := range methods { + mpos := r.pos() + mname := r.ident() + + // TODO(mdempsky): Matches bimport.go, but I + // don't agree with this. + var recv *types.Var + if base != nil { + recv = types.NewVar(token.NoPos, r.currPkg, "", base) + } + + msig := r.signature(recv, nil, nil) + methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) + } + + typ := newInterface(methods, embeddeds) + r.p.interfaceList = append(r.p.interfaceList, typ) + return typ + + case typeParamType: + if r.p.version < iexportVersionGenerics { + errorf("unexpected type param type") + } + pkg, name := r.qualifiedIdent() + id := ident{pkg, name} + if t, ok := r.p.tparamIndex[id]; ok { + // We're already in the process of importing this typeparam. + return t + } + // Otherwise, import the definition of the typeparam now. + r.p.doDecl(pkg, name) + return r.p.tparamIndex[id] + + case instanceType: + if r.p.version < iexportVersionGenerics { + errorf("unexpected instantiation type") + } + // pos does not matter for instances: they are positioned on the original + // type. + _ = r.pos() + len := r.uint64() + targs := make([]types.Type, len) + for i := range targs { + targs[i] = r.typ() + } + baseType := r.typ() + // The imported instantiated type doesn't include any methods, so + // we must always use the methods of the base (orig) type. + // TODO provide a non-nil *Environment + t, _ := typeparams.Instantiate(nil, baseType, targs, false) + return t + + case unionType: + if r.p.version < iexportVersionGenerics { + errorf("unexpected instantiation type") + } + terms := make([]*typeparams.Term, r.uint64()) + for i := range terms { + terms[i] = typeparams.NewTerm(r.bool(), r.typ()) + } + return typeparams.NewUnion(terms) + } +} + +func (r *importReader) kind() itag { + return itag(r.uint64()) +} + +func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature { + params := r.paramList() + results := r.paramList() + variadic := params.Len() > 0 && r.bool() + return typeparams.NewSignatureType(recv, rparams, tparams, params, results, variadic) +} + +func (r *importReader) tparamList() []*typeparams.TypeParam { + n := r.uint64() + if n == 0 { + return nil + } + xs := make([]*typeparams.TypeParam, n) + for i := range xs { + // Note: the standard library importer is tolerant of nil types here, + // though would panic in SetTypeParams. + xs[i] = r.typ().(*typeparams.TypeParam) + } + return xs +} + +func (r *importReader) paramList() *types.Tuple { + xs := make([]*types.Var, r.uint64()) + for i := range xs { + xs[i] = r.param() + } + return types.NewTuple(xs...) +} + +func (r *importReader) param() *types.Var { + pos := r.pos() + name := r.ident() + typ := r.typ() + return types.NewParam(pos, r.currPkg, name, typ) +} + +func (r *importReader) bool() bool { + return r.uint64() != 0 +} + +func (r *importReader) int64() int64 { + n, err := binary.ReadVarint(&r.declReader) + if err != nil { + errorf("readVarint: %v", err) + } + return n +} + +func (r *importReader) uint64() uint64 { + n, err := binary.ReadUvarint(&r.declReader) + if err != nil { + errorf("readUvarint: %v", err) + } + return n +} + +func (r *importReader) byte() byte { + x, err := r.declReader.ReadByte() + if err != nil { + errorf("declReader.ReadByte: %v", err) + } + 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 +} diff --git a/gopls/golang_org_x_tools/gcimporter/newInterface10.go b/gopls/golang_org_x_tools/gcimporter/newInterface10.go new file mode 100644 index 0000000..8b163e3 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/newInterface10.go @@ -0,0 +1,22 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.11 +// +build !go1.11 + +package gcimporter + +import "go/types" + +func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { + named := make([]*types.Named, len(embeddeds)) + for i, e := range embeddeds { + var ok bool + named[i], ok = e.(*types.Named) + if !ok { + panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11") + } + } + return types.NewInterface(methods, named) +} diff --git a/gopls/golang_org_x_tools/gcimporter/newInterface11.go b/gopls/golang_org_x_tools/gcimporter/newInterface11.go new file mode 100644 index 0000000..49984f4 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/newInterface11.go @@ -0,0 +1,14 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.11 +// +build go1.11 + +package gcimporter + +import "go/types" + +func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { + return types.NewInterfaceType(methods, embeddeds) +} diff --git a/gopls/golang_org_x_tools/gcimporter/support_go117.go b/gopls/golang_org_x_tools/gcimporter/support_go117.go new file mode 100644 index 0000000..d892273 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/support_go117.go @@ -0,0 +1,16 @@ +// 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 +} diff --git a/gopls/golang_org_x_tools/gcimporter/support_go118.go b/gopls/golang_org_x_tools/gcimporter/support_go118.go new file mode 100644 index 0000000..edbe6ea --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/support_go118.go @@ -0,0 +1,37 @@ +// 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 = iexportVersionGenerics + +// additionalPredeclared returns additional predeclared types in go.1.18. +func additionalPredeclared() []types.Type { + return []types.Type{ + // comparable + types.Universe.Lookup("comparable").Type(), + + // any + types.Universe.Lookup("any").Type(), + } +} + +// See cmd/compile/internal/types.SplitVargenSuffix. +func splitVargenSuffix(name string) (base, suffix string) { + i := len(name) + for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' { + i-- + } + const dot = "·" + if i >= len(dot) && name[i-len(dot):i] == dot { + i -= len(dot) + return name[:i], name[i:] + } + return name, "" +} diff --git a/gopls/golang_org_x_tools/gcimporter/unified_no.go b/gopls/golang_org_x_tools/gcimporter/unified_no.go new file mode 100644 index 0000000..286bf44 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/unified_no.go @@ -0,0 +1,10 @@ +// 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 && goexperiment.unified) +// +build !go1.18 !goexperiment.unified + +package gcimporter + +const unifiedIR = false diff --git a/gopls/golang_org_x_tools/gcimporter/unified_yes.go b/gopls/golang_org_x_tools/gcimporter/unified_yes.go new file mode 100644 index 0000000..b5d69ff --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/unified_yes.go @@ -0,0 +1,10 @@ +// 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 && goexperiment.unified +// +build go1.18,goexperiment.unified + +package gcimporter + +const unifiedIR = true diff --git a/gopls/golang_org_x_tools/gcimporter/ureader_no.go b/gopls/golang_org_x_tools/gcimporter/ureader_no.go new file mode 100644 index 0000000..8eb2072 --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/ureader_no.go @@ -0,0 +1,19 @@ +// 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 +} diff --git a/gopls/golang_org_x_tools/gcimporter/ureader_yes.go b/gopls/golang_org_x_tools/gcimporter/ureader_yes.go new file mode 100644 index 0000000..15108bd --- /dev/null +++ b/gopls/golang_org_x_tools/gcimporter/ureader_yes.go @@ -0,0 +1,698 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Derived from go/internal/gcimporter/ureader.go + +//go:build go1.18 +// +build go1.18 + +package gcimporter + +import ( + "go/token" + "go/types" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/pkgbits" +) + +// A pkgReader holds the shared state for reading a unified IR package +// description. +type pkgReader struct { + pkgbits.PkgDecoder + + fake fakeFileSet + + ctxt *types.Context + imports map[string]*types.Package // previously imported packages, indexed by path + + // lazily initialized arrays corresponding to the unified IR + // PosBase, Pkg, and Type sections, respectively. + posBases []string // position bases (i.e., file names) + pkgs []*types.Package + typs []types.Type + + // laterFns holds functions that need to be invoked at the end of + // import reading. + laterFns []func() + // laterFors is used in case of 'type A B' to ensure that B is processed before A. + laterFors map[types.Type]int + + // ifaces holds a list of constructed Interfaces, which need to have + // Complete called after importing is done. + ifaces []*types.Interface +} + +// later adds a function to be invoked at the end of import reading. +func (pr *pkgReader) later(fn func()) { + pr.laterFns = append(pr.laterFns, fn) +} + +// See cmd/compile/internal/noder.derivedInfo. +type derivedInfo struct { + idx pkgbits.Index + needed bool +} + +// See cmd/compile/internal/noder.typeInfo. +type typeInfo struct { + idx pkgbits.Index + derived bool +} + +func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { + s := string(data) + s = s[:strings.LastIndex(s, "\n$$\n")] + input := pkgbits.NewPkgDecoder(path, s) + pkg = readUnifiedPackage(fset, nil, imports, input) + return +} + +// laterFor adds a function to be invoked at the end of import reading, and records the type that function is finishing. +func (pr *pkgReader) laterFor(t types.Type, fn func()) { + if pr.laterFors == nil { + pr.laterFors = make(map[types.Type]int) + } + pr.laterFors[t] = len(pr.laterFns) + pr.laterFns = append(pr.laterFns, fn) +} + +// readUnifiedPackage reads a package description from the given +// unified IR export data decoder. +func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package { + pr := pkgReader{ + PkgDecoder: input, + + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*fileInfo), + }, + + ctxt: ctxt, + imports: imports, + + posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)), + pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)), + typs: make([]types.Type, input.NumElems(pkgbits.RelocType)), + } + defer pr.fake.setLines() + + r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) + pkg := r.pkg() + r.Bool() // has init + + for i, n := 0, r.Len(); i < n; i++ { + // As if r.obj(), but avoiding the Scope.Lookup call, + // to avoid eager loading of imports. + r.Sync(pkgbits.SyncObject) + assert(!r.Bool()) + r.p.objIdx(r.Reloc(pkgbits.RelocObj)) + assert(r.Len() == 0) + } + + r.Sync(pkgbits.SyncEOF) + + for _, fn := range pr.laterFns { + fn() + } + + for _, iface := range pr.ifaces { + iface.Complete() + } + + pkg.MarkComplete() + return pkg +} + +// A reader holds the state for reading a single unified IR element +// within a package. +type reader struct { + pkgbits.Decoder + + p *pkgReader + + dict *readerDict +} + +// A readerDict holds the state for type parameters that parameterize +// the current unified IR element. +type readerDict struct { + // bounds is a slice of typeInfos corresponding to the underlying + // bounds of the element's type parameters. + bounds []typeInfo + + // tparams is a slice of the constructed TypeParams for the element. + tparams []*types.TypeParam + + // devived is a slice of types derived from tparams, which may be + // instantiated while reading the current element. + derived []derivedInfo + derivedTypes []types.Type // lazily instantiated from derived +} + +func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader { + return &reader{ + Decoder: pr.NewDecoder(k, idx, marker), + p: pr, + } +} + +// @@@ Positions + +func (r *reader) pos() token.Pos { + r.Sync(pkgbits.SyncPos) + if !r.Bool() { + return token.NoPos + } + + // TODO(mdempsky): Delta encoding. + posBase := r.posBase() + line := r.Uint() + col := r.Uint() + return r.p.fake.pos(posBase, int(line), int(col)) +} + +func (r *reader) posBase() string { + return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)) +} + +func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) string { + if b := pr.posBases[idx]; b != "" { + return b + } + + r := pr.newReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase) + + // Within types2, position bases have a lot more details (e.g., + // keeping track of where //line directives appeared exactly). + // + // For go/types, we just track the file name. + + filename := r.String() + + if r.Bool() { // file base + // Was: "b = token.NewTrimmedFileBase(filename, true)" + } else { // line base + pos := r.pos() + line := r.Uint() + col := r.Uint() + + // Was: "b = token.NewLineBase(pos, filename, true, line, col)" + _, _, _ = pos, line, col + } + + b := filename + pr.posBases[idx] = b + return b +} + +// @@@ Packages + +func (r *reader) pkg() *types.Package { + r.Sync(pkgbits.SyncPkg) + return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg)) +} + +func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Package { + // TODO(mdempsky): Consider using some non-nil pointer to indicate + // the universe scope, so we don't need to keep re-reading it. + if pkg := pr.pkgs[idx]; pkg != nil { + return pkg + } + + pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg() + pr.pkgs[idx] = pkg + return pkg +} + +func (r *reader) doPkg() *types.Package { + path := r.String() + switch path { + case "": + path = r.p.PkgPath() + case "builtin": + return nil // universe + case "unsafe": + return types.Unsafe + } + + if pkg := r.p.imports[path]; pkg != nil { + return pkg + } + + name := r.String() + + pkg := types.NewPackage(path, name) + r.p.imports[path] = pkg + + imports := make([]*types.Package, r.Len()) + for i := range imports { + imports[i] = r.pkg() + } + pkg.SetImports(flattenImports(imports)) + + return pkg +} + +// flattenImports returns the transitive closure of all imported +// packages rooted from pkgs. +func flattenImports(pkgs []*types.Package) []*types.Package { + var res []*types.Package + + seen := make(map[*types.Package]bool) + var add func(pkg *types.Package) + add = func(pkg *types.Package) { + if seen[pkg] { + return + } + seen[pkg] = true + res = append(res, pkg) + for _, imp := range pkg.Imports() { + add(imp) + } + } + + for _, pkg := range pkgs { + add(pkg) + } + return res +} + +// @@@ Types + +func (r *reader) typ() types.Type { + return r.p.typIdx(r.typInfo(), r.dict) +} + +func (r *reader) typInfo() typeInfo { + r.Sync(pkgbits.SyncType) + if r.Bool() { + return typeInfo{idx: pkgbits.Index(r.Len()), derived: true} + } + return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false} +} + +func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types.Type { + idx := info.idx + var where *types.Type + if info.derived { + where = &dict.derivedTypes[idx] + idx = dict.derived[idx].idx + } else { + where = &pr.typs[idx] + } + + if typ := *where; typ != nil { + return typ + } + + r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx) + r.dict = dict + + typ := r.doTyp() + assert(typ != nil) + + // See comment in pkgReader.typIdx explaining how this happens. + if prev := *where; prev != nil { + return prev + } + + *where = typ + return typ +} + +func (r *reader) doTyp() (res types.Type) { + switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag { + default: + errorf("unhandled type tag: %v", tag) + panic("unreachable") + + case pkgbits.TypeBasic: + return types.Typ[r.Len()] + + case pkgbits.TypeNamed: + obj, targs := r.obj() + name := obj.(*types.TypeName) + if len(targs) != 0 { + t, _ := types.Instantiate(r.p.ctxt, name.Type(), targs, false) + return t + } + return name.Type() + + case pkgbits.TypeTypeParam: + return r.dict.tparams[r.Len()] + + case pkgbits.TypeArray: + len := int64(r.Uint64()) + return types.NewArray(r.typ(), len) + case pkgbits.TypeChan: + dir := types.ChanDir(r.Len()) + return types.NewChan(dir, r.typ()) + case pkgbits.TypeMap: + return types.NewMap(r.typ(), r.typ()) + case pkgbits.TypePointer: + return types.NewPointer(r.typ()) + case pkgbits.TypeSignature: + return r.signature(nil, nil, nil) + case pkgbits.TypeSlice: + return types.NewSlice(r.typ()) + case pkgbits.TypeStruct: + return r.structType() + case pkgbits.TypeInterface: + return r.interfaceType() + case pkgbits.TypeUnion: + return r.unionType() + } +} + +func (r *reader) structType() *types.Struct { + fields := make([]*types.Var, r.Len()) + var tags []string + for i := range fields { + pos := r.pos() + pkg, name := r.selector() + ftyp := r.typ() + tag := r.String() + embedded := r.Bool() + + fields[i] = types.NewField(pos, pkg, name, ftyp, embedded) + if tag != "" { + for len(tags) < i { + tags = append(tags, "") + } + tags = append(tags, tag) + } + } + return types.NewStruct(fields, tags) +} + +func (r *reader) unionType() *types.Union { + terms := make([]*types.Term, r.Len()) + for i := range terms { + terms[i] = types.NewTerm(r.Bool(), r.typ()) + } + return types.NewUnion(terms) +} + +func (r *reader) interfaceType() *types.Interface { + methods := make([]*types.Func, r.Len()) + embeddeds := make([]types.Type, r.Len()) + implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool() + + for i := range methods { + pos := r.pos() + pkg, name := r.selector() + mtyp := r.signature(nil, nil, nil) + methods[i] = types.NewFunc(pos, pkg, name, mtyp) + } + + for i := range embeddeds { + embeddeds[i] = r.typ() + } + + iface := types.NewInterfaceType(methods, embeddeds) + if implicit { + iface.MarkImplicit() + } + + // We need to call iface.Complete(), but if there are any embedded + // defined types, then we may not have set their underlying + // interface type yet. So we need to defer calling Complete until + // after we've called SetUnderlying everywhere. + // + // TODO(mdempsky): After CL 424876 lands, it should be safe to call + // iface.Complete() immediately. + r.p.ifaces = append(r.p.ifaces, iface) + + return iface +} + +func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature { + r.Sync(pkgbits.SyncSignature) + + params := r.params() + results := r.params() + variadic := r.Bool() + + return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic) +} + +func (r *reader) params() *types.Tuple { + r.Sync(pkgbits.SyncParams) + + params := make([]*types.Var, r.Len()) + for i := range params { + params[i] = r.param() + } + + return types.NewTuple(params...) +} + +func (r *reader) param() *types.Var { + r.Sync(pkgbits.SyncParam) + + pos := r.pos() + pkg, name := r.localIdent() + typ := r.typ() + + return types.NewParam(pos, pkg, name, typ) +} + +// @@@ Objects + +func (r *reader) obj() (types.Object, []types.Type) { + r.Sync(pkgbits.SyncObject) + + assert(!r.Bool()) + + pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj)) + obj := pkgScope(pkg).Lookup(name) + + targs := make([]types.Type, r.Len()) + for i := range targs { + targs[i] = r.typ() + } + + return obj, targs +} + +func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { + rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1) + + objPkg, objName := rname.qualifiedIdent() + assert(objName != "") + + tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj)) + + if tag == pkgbits.ObjStub { + assert(objPkg == nil || objPkg == types.Unsafe) + return objPkg, objName + } + + // Ignore local types promoted to global scope (#55110). + if _, suffix := splitVargenSuffix(objName); suffix != "" { + return objPkg, objName + } + + if objPkg.Scope().Lookup(objName) == nil { + dict := pr.objDictIdx(idx) + + r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1) + r.dict = dict + + declare := func(obj types.Object) { + objPkg.Scope().Insert(obj) + } + + switch tag { + default: + panic("weird") + + case pkgbits.ObjAlias: + pos := r.pos() + typ := r.typ() + declare(types.NewTypeName(pos, objPkg, objName, typ)) + + case pkgbits.ObjConst: + pos := r.pos() + typ := r.typ() + val := r.Value() + declare(types.NewConst(pos, objPkg, objName, typ, val)) + + case pkgbits.ObjFunc: + pos := r.pos() + tparams := r.typeParamNames() + sig := r.signature(nil, nil, tparams) + declare(types.NewFunc(pos, objPkg, objName, sig)) + + case pkgbits.ObjType: + pos := r.pos() + + obj := types.NewTypeName(pos, objPkg, objName, nil) + named := types.NewNamed(obj, nil, nil) + declare(obj) + + named.SetTypeParams(r.typeParamNames()) + + rhs := r.typ() + pk := r.p + pk.laterFor(named, func() { + // First be sure that the rhs is initialized, if it needs to be initialized. + delete(pk.laterFors, named) // prevent cycles + if i, ok := pk.laterFors[rhs]; ok { + f := pk.laterFns[i] + pk.laterFns[i] = func() {} // function is running now, so replace it with a no-op + f() // initialize RHS + } + underlying := rhs.Underlying() + + // If the underlying type is an interface, we need to + // duplicate its methods so we can replace the receiver + // parameter's type (#49906). + if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 { + methods := make([]*types.Func, iface.NumExplicitMethods()) + for i := range methods { + fn := iface.ExplicitMethod(i) + sig := fn.Type().(*types.Signature) + + recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named) + methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignature(recv, sig.Params(), sig.Results(), sig.Variadic())) + } + + embeds := make([]types.Type, iface.NumEmbeddeds()) + for i := range embeds { + embeds[i] = iface.EmbeddedType(i) + } + + newIface := types.NewInterfaceType(methods, embeds) + r.p.ifaces = append(r.p.ifaces, newIface) + underlying = newIface + } + + named.SetUnderlying(underlying) + }) + + for i, n := 0, r.Len(); i < n; i++ { + named.AddMethod(r.method()) + } + + case pkgbits.ObjVar: + pos := r.pos() + typ := r.typ() + declare(types.NewVar(pos, objPkg, objName, typ)) + } + } + + return objPkg, objName +} + +func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict { + r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1) + + var dict readerDict + + if implicits := r.Len(); implicits != 0 { + errorf("unexpected object with %v implicit type parameter(s)", implicits) + } + + dict.bounds = make([]typeInfo, r.Len()) + for i := range dict.bounds { + dict.bounds[i] = r.typInfo() + } + + dict.derived = make([]derivedInfo, r.Len()) + dict.derivedTypes = make([]types.Type, len(dict.derived)) + for i := range dict.derived { + dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()} + } + + // function references follow, but reader doesn't need those + + return &dict +} + +func (r *reader) typeParamNames() []*types.TypeParam { + r.Sync(pkgbits.SyncTypeParamNames) + + // Note: This code assumes it only processes objects without + // implement type parameters. This is currently fine, because + // reader is only used to read in exported declarations, which are + // always package scoped. + + if len(r.dict.bounds) == 0 { + return nil + } + + // Careful: Type parameter lists may have cycles. To allow for this, + // we construct the type parameter list in two passes: first we + // create all the TypeNames and TypeParams, then we construct and + // set the bound type. + + r.dict.tparams = make([]*types.TypeParam, len(r.dict.bounds)) + for i := range r.dict.bounds { + pos := r.pos() + pkg, name := r.localIdent() + + tname := types.NewTypeName(pos, pkg, name, nil) + r.dict.tparams[i] = types.NewTypeParam(tname, nil) + } + + typs := make([]types.Type, len(r.dict.bounds)) + for i, bound := range r.dict.bounds { + typs[i] = r.p.typIdx(bound, r.dict) + } + + // TODO(mdempsky): This is subtle, elaborate further. + // + // We have to save tparams outside of the closure, because + // typeParamNames() can be called multiple times with the same + // dictionary instance. + // + // Also, this needs to happen later to make sure SetUnderlying has + // been called. + // + // TODO(mdempsky): Is it safe to have a single "later" slice or do + // we need to have multiple passes? See comments on CL 386002 and + // go.dev/issue/52104. + tparams := r.dict.tparams + r.p.later(func() { + for i, typ := range typs { + tparams[i].SetConstraint(typ) + } + }) + + return r.dict.tparams +} + +func (r *reader) method() *types.Func { + r.Sync(pkgbits.SyncMethod) + pos := r.pos() + pkg, name := r.selector() + + rparams := r.typeParamNames() + sig := r.signature(r.param(), rparams, nil) + + _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go. + return types.NewFunc(pos, pkg, name, sig) +} + +func (r *reader) qualifiedIdent() (*types.Package, string) { return r.ident(pkgbits.SyncSym) } +func (r *reader) localIdent() (*types.Package, string) { return r.ident(pkgbits.SyncLocalIdent) } +func (r *reader) selector() (*types.Package, string) { return r.ident(pkgbits.SyncSelector) } + +func (r *reader) ident(marker pkgbits.SyncMarker) (*types.Package, string) { + r.Sync(marker) + return r.pkg(), r.String() +} + +// pkgScope returns pkg.Scope(). +// If pkg is nil, it returns types.Universe instead. +// +// TODO(mdempsky): Remove after x/tools can depend on Go 1.19. +func pkgScope(pkg *types.Package) *types.Scope { + if pkg != nil { + return pkg.Scope() + } + return types.Universe +} diff --git a/gopls/golang_org_x_tools/gocommand/invoke.go b/gopls/golang_org_x_tools/gocommand/invoke.go new file mode 100644 index 0000000..49ff2c5 --- /dev/null +++ b/gopls/golang_org_x_tools/gocommand/invoke.go @@ -0,0 +1,356 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gocommand is a helper for calling the go command. +package gocommand + +import ( + "bytes" + "context" + "fmt" + "io" + "log" + "os" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "time" + + exec "golang.org/x/sys/execabs" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +// An Runner will run go command invocations and serialize +// them if it sees a concurrency error. +type Runner struct { + // once guards the runner initialization. + once sync.Once + + // inFlight tracks available workers. + inFlight chan struct{} + + // serialized guards the ability to run a go command serially, + // to avoid deadlocks when claiming workers. + serialized chan struct{} +} + +const maxInFlight = 10 + +func (runner *Runner) initialize() { + runner.once.Do(func() { + runner.inFlight = make(chan struct{}, maxInFlight) + runner.serialized = make(chan struct{}, 1) + }) +} + +// 1.13: go: updates to go.mod needed, but contents have changed +// 1.14: go: updating go.mod: existing contents have changed since last read +var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`) + +// Run is a convenience wrapper around RunRaw. +// It returns only stdout and a "friendly" error. +func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) { + stdout, _, friendly, _ := runner.RunRaw(ctx, inv) + return stdout, friendly +} + +// RunPiped runs the invocation serially, always waiting for any concurrent +// invocations to complete first. +func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error { + _, err := runner.runPiped(ctx, inv, stdout, stderr) + return err +} + +// RunRaw runs the invocation, serializing requests only if they fight over +// go.mod changes. +func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { + // Make sure the runner is always initialized. + runner.initialize() + + // First, try to run the go command concurrently. + stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) + + // If we encounter a load concurrency error, we need to retry serially. + if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { + return stdout, stderr, friendlyErr, err + } + event.Error(ctx, "Load concurrency error, will retry serially", err) + + // Run serially by calling runPiped. + stdout.Reset() + stderr.Reset() + friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) + return stdout, stderr, friendlyErr, err +} + +func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { + // Wait for 1 worker to become available. + select { + case <-ctx.Done(): + return nil, nil, nil, ctx.Err() + case runner.inFlight <- struct{}{}: + defer func() { <-runner.inFlight }() + } + + stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} + friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr) + return stdout, stderr, friendlyErr, err +} + +func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { + // Make sure the runner is always initialized. + runner.initialize() + + // Acquire the serialization lock. This avoids deadlocks between two + // runPiped commands. + select { + case <-ctx.Done(): + return nil, ctx.Err() + case runner.serialized <- struct{}{}: + defer func() { <-runner.serialized }() + } + + // Wait for all in-progress go commands to return before proceeding, + // to avoid load concurrency errors. + for i := 0; i < maxInFlight; i++ { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case runner.inFlight <- struct{}{}: + // Make sure we always "return" any workers we took. + defer func() { <-runner.inFlight }() + } + } + + return inv.runWithFriendlyError(ctx, stdout, stderr) +} + +// An Invocation represents a call to the go command. +type Invocation struct { + Verb string + Args []string + BuildFlags []string + + // If ModFlag is set, the go command is invoked with -mod=ModFlag. + ModFlag string + + // If ModFile is set, the go command is invoked with -modfile=ModFile. + ModFile string + + // If Overlay is set, the go command is invoked with -overlay=Overlay. + Overlay string + + // If CleanEnv is set, the invocation will run only with the environment + // in Env, not starting with os.Environ. + CleanEnv bool + Env []string + WorkingDir string + Logf func(format string, args ...interface{}) +} + +func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { + rawError = i.run(ctx, stdout, stderr) + if rawError != nil { + friendlyError = rawError + // Check for 'go' executable not being found. + if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound { + friendlyError = fmt.Errorf("go command required, not found: %v", ee) + } + if ctx.Err() != nil { + friendlyError = ctx.Err() + } + friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr) + } + return +} + +func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { + log := i.Logf + if log == nil { + log = func(string, ...interface{}) {} + } + + goArgs := []string{i.Verb} + + appendModFile := func() { + if i.ModFile != "" { + goArgs = append(goArgs, "-modfile="+i.ModFile) + } + } + appendModFlag := func() { + if i.ModFlag != "" { + goArgs = append(goArgs, "-mod="+i.ModFlag) + } + } + appendOverlayFlag := func() { + if i.Overlay != "" { + goArgs = append(goArgs, "-overlay="+i.Overlay) + } + } + + switch i.Verb { + case "env", "version": + goArgs = append(goArgs, i.Args...) + case "mod": + // mod needs the sub-verb before flags. + goArgs = append(goArgs, i.Args[0]) + appendModFile() + goArgs = append(goArgs, i.Args[1:]...) + case "get": + goArgs = append(goArgs, i.BuildFlags...) + appendModFile() + goArgs = append(goArgs, i.Args...) + + default: // notably list and build. + goArgs = append(goArgs, i.BuildFlags...) + appendModFile() + appendModFlag() + appendOverlayFlag() + goArgs = append(goArgs, i.Args...) + } + cmd := exec.Command("go", goArgs...) + cmd.Stdout = stdout + cmd.Stderr = stderr + // On darwin the cwd gets resolved to the real path, which breaks anything that + // expects the working directory to keep the original path, including the + // go command when dealing with modules. + // The Go stdlib has a special feature where if the cwd and the PWD are the + // same node then it trusts the PWD, so by setting it in the env for the child + // process we fix up all the paths returned by the go command. + if !i.CleanEnv { + cmd.Env = os.Environ() + } + cmd.Env = append(cmd.Env, i.Env...) + if i.WorkingDir != "" { + cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir) + cmd.Dir = i.WorkingDir + } + defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) + + return runCmdContext(ctx, cmd) +} + +// DebugHangingGoCommands may be set by tests to enable additional +// instrumentation (including panics) for debugging hanging Go commands. +// +// See golang/go#54461 for details. +var DebugHangingGoCommands = false + +// runCmdContext is like exec.CommandContext except it sends os.Interrupt +// before os.Kill. +func runCmdContext(ctx context.Context, cmd *exec.Cmd) error { + if err := cmd.Start(); err != nil { + return err + } + resChan := make(chan error, 1) + go func() { + resChan <- cmd.Wait() + }() + + // If we're interested in debugging hanging Go commands, stop waiting after a + // minute and panic with interesting information. + if DebugHangingGoCommands { + select { + case err := <-resChan: + return err + case <-time.After(1 * time.Minute): + HandleHangingGoCommand(cmd.Process) + case <-ctx.Done(): + } + } else { + select { + case err := <-resChan: + return err + case <-ctx.Done(): + } + } + + // Cancelled. Interrupt and see if it ends voluntarily. + cmd.Process.Signal(os.Interrupt) + select { + case err := <-resChan: + return err + case <-time.After(time.Second): + } + + // Didn't shut down in response to interrupt. Kill it hard. + // TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT + // on certain platforms, such as unix. + if err := cmd.Process.Kill(); err != nil && DebugHangingGoCommands { + // Don't panic here as this reliably fails on windows with EINVAL. + log.Printf("error killing the Go command: %v", err) + } + + // See above: don't wait indefinitely if we're debugging hanging Go commands. + if DebugHangingGoCommands { + select { + case err := <-resChan: + return err + case <-time.After(10 * time.Second): // a shorter wait as resChan should return quickly following Kill + HandleHangingGoCommand(cmd.Process) + } + } + return <-resChan +} + +func HandleHangingGoCommand(proc *os.Process) { + switch runtime.GOOS { + case "linux", "darwin", "freebsd", "netbsd": + fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND + +The gopls test runner has detected a hanging go command. In order to debug +this, the output of ps and lsof/fstat is printed below. + +See golang/go#54461 for more details.`) + + fmt.Fprintln(os.Stderr, "\nps axo ppid,pid,command:") + fmt.Fprintln(os.Stderr, "-------------------------") + psCmd := exec.Command("ps", "axo", "ppid,pid,command") + psCmd.Stdout = os.Stderr + psCmd.Stderr = os.Stderr + if err := psCmd.Run(); err != nil { + panic(fmt.Sprintf("running ps: %v", err)) + } + + listFiles := "lsof" + if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" { + listFiles = "fstat" + } + + fmt.Fprintln(os.Stderr, "\n"+listFiles+":") + fmt.Fprintln(os.Stderr, "-----") + listFilesCmd := exec.Command(listFiles) + listFilesCmd.Stdout = os.Stderr + listFilesCmd.Stderr = os.Stderr + if err := listFilesCmd.Run(); err != nil { + panic(fmt.Sprintf("running %s: %v", listFiles, err)) + } + } + panic(fmt.Sprintf("detected hanging go command (pid %d): see golang/go#54461 for more details", proc.Pid)) +} + +func cmdDebugStr(cmd *exec.Cmd) string { + env := make(map[string]string) + for _, kv := range cmd.Env { + split := strings.SplitN(kv, "=", 2) + if len(split) == 2 { + k, v := split[0], split[1] + env[k] = v + } + } + + var args []string + for _, arg := range cmd.Args { + quoted := strconv.Quote(arg) + if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") { + args = append(args, quoted) + } else { + args = append(args, arg) + } + } + return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " ")) +} diff --git a/gopls/golang_org_x_tools/gocommand/vendor.go b/gopls/golang_org_x_tools/gocommand/vendor.go new file mode 100644 index 0000000..2d3d408 --- /dev/null +++ b/gopls/golang_org_x_tools/gocommand/vendor.go @@ -0,0 +1,109 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocommand + +import ( + "bytes" + "context" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + "time" + + "golang.org/x/mod/semver" +) + +// ModuleJSON holds information about a module. +type ModuleJSON struct { + Path string // module path + Version string // module version + Versions []string // available module versions (with -versions) + Replace *ModuleJSON // replaced by this module + Time *time.Time // time version was created + Update *ModuleJSON // available update, if any (with -u) + Main bool // is this the main module? + Indirect bool // is this module only an indirect dependency of main module? + Dir string // directory holding files for this module, if any + GoMod string // path to go.mod file used when loading this module, if any + GoVersion string // go version used in module +} + +var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`) + +// VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands +// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields, +// of which only Verb and Args are modified to run the appropriate Go command. +// Inspired by setDefaultBuildMod in modload/init.go +func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, *ModuleJSON, error) { + mainMod, go114, err := getMainModuleAnd114(ctx, inv, r) + if err != nil { + return false, nil, err + } + + // We check the GOFLAGS to see if there is anything overridden or not. + inv.Verb = "env" + inv.Args = []string{"GOFLAGS"} + stdout, err := r.Run(ctx, inv) + if err != nil { + return false, nil, err + } + goflags := string(bytes.TrimSpace(stdout.Bytes())) + matches := modFlagRegexp.FindStringSubmatch(goflags) + var modFlag string + if len(matches) != 0 { + modFlag = matches[1] + } + // Don't override an explicit '-mod=' argument. + if modFlag == "vendor" { + return true, mainMod, nil + } else if modFlag != "" { + return false, nil, nil + } + if mainMod == nil || !go114 { + return false, nil, nil + } + // Check 1.14's automatic vendor mode. + if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() { + if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 { + // The Go version is at least 1.14, and a vendor directory exists. + // Set -mod=vendor by default. + return true, mainMod, nil + } + } + return false, nil, nil +} + +// getMainModuleAnd114 gets one of the main modules' information and whether the +// go command in use is 1.14+. This is the information needed to figure out +// if vendoring should be enabled. +func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { + const format = `{{.Path}} +{{.Dir}} +{{.GoMod}} +{{.GoVersion}} +{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}} +` + inv.Verb = "list" + inv.Args = []string{"-m", "-f", format} + stdout, err := r.Run(ctx, inv) + if err != nil { + return nil, false, err + } + + lines := strings.Split(stdout.String(), "\n") + if len(lines) < 5 { + return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String()) + } + mod := &ModuleJSON{ + Path: lines[0], + Dir: lines[1], + GoMod: lines[2], + GoVersion: lines[3], + Main: true, + } + return mod, lines[4] == "go1.14", nil +} diff --git a/gopls/golang_org_x_tools/gocommand/version.go b/gopls/golang_org_x_tools/gocommand/version.go new file mode 100644 index 0000000..8db5ceb --- /dev/null +++ b/gopls/golang_org_x_tools/gocommand/version.go @@ -0,0 +1,58 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocommand + +import ( + "context" + "fmt" + "strings" +) + +// GoVersion reports the minor version number of the highest release +// tag built into the go command on the PATH. +// +// Note that this may be higher than the version of the go tool used +// to build this application, and thus the versions of the standard +// go/{scanner,parser,ast,types} packages that are linked into it. +// In that case, callers should either downgrade to the version of +// go used to build the application, or report an error that the +// application is too old to use the go command on the PATH. +func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) { + inv.Verb = "list" + inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`} + inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off") + // Unset any unneeded flags, and remove them from BuildFlags, if they're + // present. + inv.ModFile = "" + inv.ModFlag = "" + var buildFlags []string + for _, flag := range inv.BuildFlags { + // Flags can be prefixed by one or two dashes. + f := strings.TrimPrefix(strings.TrimPrefix(flag, "-"), "-") + if strings.HasPrefix(f, "mod=") || strings.HasPrefix(f, "modfile=") { + continue + } + buildFlags = append(buildFlags, flag) + } + inv.BuildFlags = buildFlags + stdoutBytes, err := r.Run(ctx, inv) + if err != nil { + return 0, err + } + stdout := stdoutBytes.String() + if len(stdout) < 3 { + return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout) + } + // Split up "[go1.1 go1.15]" and return highest go1.X value. + tags := strings.Fields(stdout[1 : len(stdout)-2]) + for i := len(tags) - 1; i >= 0; i-- { + var version int + if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil { + continue + } + return version, nil + } + return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags) +} diff --git a/gopls/golang_org_x_tools/gopathwalk/walk.go b/gopls/golang_org_x_tools/gopathwalk/walk.go new file mode 100644 index 0000000..4af5528 --- /dev/null +++ b/gopls/golang_org_x_tools/gopathwalk/walk.go @@ -0,0 +1,254 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gopathwalk is like filepath.Walk but specialized for finding Go +// packages, particularly in $GOPATH and $GOROOT. +package gopathwalk + +import ( + "bufio" + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + "time" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/fastwalk" +) + +// Options controls the behavior of a Walk call. +type Options struct { + // If Logf is non-nil, debug logging is enabled through this function. + Logf func(format string, args ...interface{}) + // Search module caches. Also disables legacy goimports ignore rules. + ModulesEnabled bool +} + +// RootType indicates the type of a Root. +type RootType int + +const ( + RootUnknown RootType = iota + RootGOROOT + RootGOPATH + RootCurrentModule + RootModuleCache + RootOther +) + +// A Root is a starting point for a Walk. +type Root struct { + Path string + Type RootType +} + +// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. +// For each package found, add will be called (concurrently) with the absolute +// paths of the containing source directory and the package directory. +// add will be called concurrently. +func Walk(roots []Root, add func(root Root, dir string), opts Options) { + WalkSkip(roots, add, func(Root, string) bool { return false }, opts) +} + +// WalkSkip walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. +// For each package found, add will be called (concurrently) with the absolute +// paths of the containing source directory and the package directory. +// For each directory that will be scanned, skip will be called (concurrently) +// with the absolute paths of the containing source directory and the directory. +// If skip returns false on a directory it will be processed. +// add will be called concurrently. +// skip will be called concurrently. +func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root, dir string) bool, opts Options) { + for _, root := range roots { + walkDir(root, add, skip, opts) + } +} + +// 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) { + if _, err := os.Stat(root.Path); os.IsNotExist(err) { + if opts.Logf != nil { + opts.Logf("skipping nonexistent directory: %v", root.Path) + } + return + } + start := time.Now() + if opts.Logf != nil { + opts.Logf("gopathwalk: scanning %s", root.Path) + } + w := &walker{ + root: root, + add: add, + skip: skip, + opts: opts, + } + w.init() + if err := fastwalk.Walk(root.Path, w.walk); err != nil { + log.Printf("gopathwalk: scanning directory %v: %v", root.Path, err) + } + + if opts.Logf != nil { + opts.Logf("gopathwalk: scanned %s in %v", root.Path, time.Since(start)) + } +} + +// walker is the callback for fastwalk.Walk. +type walker struct { + root Root // The source directory to scan. + add func(Root, string) // The callback that will be invoked for every possible Go package dir. + 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. + + ignoredDirs []os.FileInfo // The ignored directories, loaded from .goimportsignore files. +} + +// init initializes the walker based on its Options +func (w *walker) init() { + var ignoredPaths []string + if w.root.Type == RootModuleCache { + ignoredPaths = []string{"cache"} + } + if !w.opts.ModulesEnabled && w.root.Type == RootGOPATH { + ignoredPaths = w.getIgnoredDirs(w.root.Path) + ignoredPaths = append(ignoredPaths, "v", "mod") + } + + for _, p := range ignoredPaths { + full := filepath.Join(w.root.Path, p) + if fi, err := os.Stat(full); err == nil { + w.ignoredDirs = append(w.ignoredDirs, fi) + if w.opts.Logf != nil { + w.opts.Logf("Directory added to ignore list: %s", full) + } + } else if w.opts.Logf != nil { + w.opts.Logf("Error statting ignored directory: %v", err) + } + } +} + +// getIgnoredDirs reads an optional config file at /.goimportsignore +// of relative directories to ignore when scanning for go files. +// The provided path is one of the $GOPATH entries with "src" appended. +func (w *walker) getIgnoredDirs(path string) []string { + file := filepath.Join(path, ".goimportsignore") + slurp, err := ioutil.ReadFile(file) + if w.opts.Logf != nil { + if err != nil { + w.opts.Logf("%v", err) + } else { + w.opts.Logf("Read %s", file) + } + } + if err != nil { + return nil + } + + var ignoredDirs []string + bs := bufio.NewScanner(bytes.NewReader(slurp)) + for bs.Scan() { + line := strings.TrimSpace(bs.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + ignoredDirs = append(ignoredDirs, line) + } + return ignoredDirs +} + +// shouldSkipDir reports whether the file should be skipped or not. +func (w *walker) shouldSkipDir(fi os.FileInfo, dir string) bool { + for _, ignoredDir := range w.ignoredDirs { + if os.SameFile(fi, ignoredDir) { + return true + } + } + if w.skip != nil { + // Check with the user specified callback. + return w.skip(w.root, dir) + } + return false +} + +// walk walks through the given path. +func (w *walker) walk(path string, typ os.FileMode) error { + if typ.IsRegular() { + 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 fastwalk.ErrSkipFiles + } + if !strings.HasSuffix(path, ".go") { + return nil + } + + w.add(w.root, dir) + return fastwalk.ErrSkipFiles + } + if typ == os.ModeDir { + 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 filepath.SkipDir + } + fi, err := os.Lstat(path) + if err == nil && w.shouldSkipDir(fi, path) { + return filepath.SkipDir + } + return nil + } + if typ == os.ModeSymlink { + base := filepath.Base(path) + if strings.HasPrefix(base, ".#") { + // Emacs noise. + return nil + } + if w.shouldTraverse(path) { + return fastwalk.ErrTraverseLink + } + } + return nil +} + +// shouldTraverse reports whether the symlink fi, found in dir, +// should be followed. It makes sure symlinks were never visited +// before to avoid symlink loops. +func (w *walker) shouldTraverse(path string) bool { + ts, err := os.Stat(path) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return false + } + if !ts.IsDir() { + return false + } + if w.shouldSkipDir(ts, filepath.Dir(path)) { + return false + } + // Check for symlink loops by statting each directory component + // and seeing if any are the same file as ts. + for { + parent := filepath.Dir(path) + if parent == path { + // Made it to the root without seeing a cycle. + // Use this symlink. + return true + } + parentInfo, err := os.Stat(parent) + if err != nil { + return false + } + if os.SameFile(ts, parentInfo) { + // Cycle. Don't traverse. + return false + } + path = parent + } + +} diff --git a/gopls/golang_org_x_tools/goroot/importcfg.go b/gopls/golang_org_x_tools/goroot/importcfg.go new file mode 100644 index 0000000..6575cfb --- /dev/null +++ b/gopls/golang_org_x_tools/goroot/importcfg.go @@ -0,0 +1,71 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package goroot is a copy of package internal/goroot +// in the main GO repot. It provides a utility to produce +// an importcfg and import path to package file map mapping +// standard library packages to the locations of their export +// data files. +package goroot + +import ( + "bytes" + "fmt" + "os/exec" + "strings" + "sync" +) + +// Importcfg returns an importcfg file to be passed to the +// Go compiler that contains the cached paths for the .a files for the +// standard library. +func Importcfg() (string, error) { + var icfg bytes.Buffer + + m, err := PkgfileMap() + if err != nil { + return "", err + } + fmt.Fprintf(&icfg, "# import config") + for importPath, export := range m { + if importPath != "unsafe" && export != "" { // unsafe + fmt.Fprintf(&icfg, "\npackagefile %s=%s", importPath, export) + } + } + s := icfg.String() + return s, nil +} + +var ( + stdlibPkgfileMap map[string]string + stdlibPkgfileErr error + once sync.Once +) + +// PkgfileMap returns a map of package paths to the location on disk +// of the .a file for the package. +// The caller must not modify the map. +func PkgfileMap() (map[string]string, error) { + once.Do(func() { + m := make(map[string]string) + output, err := exec.Command("go", "list", "-export", "-e", "-f", "{{.ImportPath}} {{.Export}}", "std", "cmd").Output() + if err != nil { + stdlibPkgfileErr = err + } + for _, line := range strings.Split(string(output), "\n") { + if line == "" { + continue + } + sp := strings.SplitN(line, " ", 2) + if len(sp) != 2 { + err = fmt.Errorf("determining pkgfile map: invalid line in go list output: %q", line) + return + } + importPath, export := sp[0], sp[1] + m[importPath] = export + } + stdlibPkgfileMap = m + }) + return stdlibPkgfileMap, stdlibPkgfileErr +} diff --git a/gopls/golang_org_x_tools/imports/fix.go b/gopls/golang_org_x_tools/imports/fix.go new file mode 100644 index 0000000..c545bca --- /dev/null +++ b/gopls/golang_org_x_tools/imports/fix.go @@ -0,0 +1,1735 @@ +// Copyright 2013 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 imports + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "io/ioutil" + "os" + "path" + "path/filepath" + "reflect" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" + + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gocommand" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gopathwalk" +) + +// importToGroup is a list of functions which map from an import path to +// a group number. +var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){ + func(localPrefix, importPath string) (num int, ok bool) { + if localPrefix == "" { + return + } + for _, p := range strings.Split(localPrefix, ",") { + if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath { + return 3, true + } + } + return + }, + func(_, importPath string) (num int, ok bool) { + if strings.HasPrefix(importPath, "appengine") { + return 2, true + } + return + }, + func(_, importPath string) (num int, ok bool) { + firstComponent := strings.Split(importPath, "/")[0] + if strings.Contains(firstComponent, ".") { + return 1, true + } + return + }, +} + +func importGroup(localPrefix, importPath string) int { + for _, fn := range importToGroup { + if n, ok := fn(localPrefix, importPath); ok { + return n + } + } + return 0 +} + +type ImportFixType int + +const ( + AddImport ImportFixType = iota + DeleteImport + SetImportName +) + +type ImportFix struct { + // StmtInfo represents the import statement this fix will add, remove, or change. + StmtInfo ImportInfo + // IdentName is the identifier that this fix will add or remove. + IdentName string + // FixType is the type of fix this is (AddImport, DeleteImport, SetImportName). + FixType ImportFixType + Relevance float64 // see pkg +} + +// An ImportInfo represents a single import statement. +type ImportInfo struct { + ImportPath string // import path, e.g. "crypto/rand". + Name string // import name, e.g. "crand", or "" if none. +} + +// A packageInfo represents what's known about a package. +type packageInfo struct { + name string // real package name, if known. + exports map[string]bool // known exports. +} + +// parseOtherFiles parses all the Go files in srcDir except filename, including +// test files if filename looks like a test. +func parseOtherFiles(fset *token.FileSet, srcDir, filename string) []*ast.File { + // This could use go/packages but it doesn't buy much, and it fails + // with https://golang.org/issue/26296 in LoadFiles mode in some cases. + considerTests := strings.HasSuffix(filename, "_test.go") + + fileBase := filepath.Base(filename) + packageFileInfos, err := ioutil.ReadDir(srcDir) + if err != nil { + return nil + } + + var files []*ast.File + for _, fi := range packageFileInfos { + if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") { + continue + } + if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") { + continue + } + + f, err := parser.ParseFile(fset, filepath.Join(srcDir, fi.Name()), nil, 0) + if err != nil { + continue + } + + files = append(files, f) + } + + return files +} + +// addGlobals puts the names of package vars into the provided map. +func addGlobals(f *ast.File, globals map[string]bool) { + for _, decl := range f.Decls { + genDecl, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + + for _, spec := range genDecl.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + globals[valueSpec.Names[0].Name] = true + } + } +} + +// collectReferences builds a map of selector expressions, from +// left hand side (X) to a set of right hand sides (Sel). +func collectReferences(f *ast.File) references { + refs := references{} + + var visitor visitFn + visitor = func(node ast.Node) ast.Visitor { + if node == nil { + return visitor + } + switch v := node.(type) { + case *ast.SelectorExpr: + xident, ok := v.X.(*ast.Ident) + if !ok { + break + } + if xident.Obj != nil { + // If the parser can resolve it, it's not a package ref. + break + } + if !ast.IsExported(v.Sel.Name) { + // Whatever this is, it's not exported from a package. + break + } + pkgName := xident.Name + r := refs[pkgName] + if r == nil { + r = make(map[string]bool) + refs[pkgName] = r + } + r[v.Sel.Name] = true + } + return visitor + } + ast.Walk(visitor, f) + return refs +} + +// collectImports returns all the imports in f. +// Unnamed imports (., _) and "C" are ignored. +func collectImports(f *ast.File) []*ImportInfo { + var imports []*ImportInfo + for _, imp := range f.Imports { + var name string + if imp.Name != nil { + name = imp.Name.Name + } + if imp.Path.Value == `"C"` || name == "_" || name == "." { + continue + } + path := strings.Trim(imp.Path.Value, `"`) + imports = append(imports, &ImportInfo{ + Name: name, + ImportPath: path, + }) + } + return imports +} + +// findMissingImport searches pass's candidates for an import that provides +// pkg, containing all of syms. +func (p *pass) findMissingImport(pkg string, syms map[string]bool) *ImportInfo { + for _, candidate := range p.candidates { + pkgInfo, ok := p.knownPackages[candidate.ImportPath] + if !ok { + continue + } + if p.importIdentifier(candidate) != pkg { + continue + } + + allFound := true + for right := range syms { + if !pkgInfo.exports[right] { + allFound = false + break + } + } + + if allFound { + return candidate + } + } + return nil +} + +// references is set of references found in a Go file. The first map key is the +// left hand side of a selector expression, the second key is the right hand +// side, and the value should always be true. +type references map[string]map[string]bool + +// A pass contains all the inputs and state necessary to fix a file's imports. +// It can be modified in some ways during use; see comments below. +type pass struct { + // Inputs. These must be set before a call to load, and not modified after. + fset *token.FileSet // fset used to parse f and its siblings. + f *ast.File // the file being fixed. + srcDir string // the directory containing f. + env *ProcessEnv // the environment to use for go commands, etc. + loadRealPackageNames bool // if true, load package names from disk rather than guessing them. + otherFiles []*ast.File // sibling files. + + // Intermediate state, generated by load. + existingImports map[string]*ImportInfo + allRefs references + missingRefs references + + // Inputs to fix. These can be augmented between successive fix calls. + lastTry bool // indicates that this is the last call and fix should clean up as best it can. + candidates []*ImportInfo // candidate imports in priority order. + knownPackages map[string]*packageInfo // information about all known packages. +} + +// loadPackageNames saves the package names for everything referenced by imports. +func (p *pass) loadPackageNames(imports []*ImportInfo) error { + if p.env.Logf != nil { + p.env.Logf("loading package names for %v packages", len(imports)) + defer func() { + p.env.Logf("done loading package names for %v packages", len(imports)) + }() + } + var unknown []string + for _, imp := range imports { + if _, ok := p.knownPackages[imp.ImportPath]; ok { + continue + } + unknown = append(unknown, imp.ImportPath) + } + + resolver, err := p.env.GetResolver() + if err != nil { + return err + } + + names, err := resolver.loadPackageNames(unknown, p.srcDir) + if err != nil { + return err + } + + for path, name := range names { + p.knownPackages[path] = &packageInfo{ + name: name, + exports: map[string]bool{}, + } + } + return nil +} + +// importIdentifier returns the identifier that imp will introduce. It will +// guess if the package name has not been loaded, e.g. because the source +// is not available. +func (p *pass) importIdentifier(imp *ImportInfo) string { + if imp.Name != "" { + return imp.Name + } + known := p.knownPackages[imp.ImportPath] + if known != nil && known.name != "" { + return known.name + } + return ImportPathToAssumedName(imp.ImportPath) +} + +// load reads in everything necessary to run a pass, and reports whether the +// file already has all the imports it needs. It fills in p.missingRefs with the +// file's missing symbols, if any, or removes unused imports if not. +func (p *pass) load() ([]*ImportFix, bool) { + p.knownPackages = map[string]*packageInfo{} + p.missingRefs = references{} + p.existingImports = map[string]*ImportInfo{} + + // Load basic information about the file in question. + p.allRefs = collectReferences(p.f) + + // Load stuff from other files in the same package: + // global variables so we know they don't need resolving, and imports + // that we might want to mimic. + globals := map[string]bool{} + for _, otherFile := range p.otherFiles { + // Don't load globals from files that are in the same directory + // but a different package. Using them to suggest imports is OK. + if p.f.Name.Name == otherFile.Name.Name { + addGlobals(otherFile, globals) + } + p.candidates = append(p.candidates, collectImports(otherFile)...) + } + + // Resolve all the import paths we've seen to package names, and store + // f's imports by the identifier they introduce. + imports := collectImports(p.f) + if p.loadRealPackageNames { + err := p.loadPackageNames(append(imports, p.candidates...)) + if err != nil { + if p.env.Logf != nil { + p.env.Logf("loading package names: %v", err) + } + return nil, false + } + } + for _, imp := range imports { + p.existingImports[p.importIdentifier(imp)] = imp + } + + // Find missing references. + for left, rights := range p.allRefs { + if globals[left] { + continue + } + _, ok := p.existingImports[left] + if !ok { + p.missingRefs[left] = rights + continue + } + } + if len(p.missingRefs) != 0 { + return nil, false + } + + return p.fix() +} + +// fix attempts to satisfy missing imports using p.candidates. If it finds +// everything, or if p.lastTry is true, it updates fixes to add the imports it found, +// delete anything unused, and update import names, and returns true. +func (p *pass) fix() ([]*ImportFix, bool) { + // Find missing imports. + var selected []*ImportInfo + for left, rights := range p.missingRefs { + if imp := p.findMissingImport(left, rights); imp != nil { + selected = append(selected, imp) + } + } + + if !p.lastTry && len(selected) != len(p.missingRefs) { + return nil, false + } + + // Found everything, or giving up. Add the new imports and remove any unused. + var fixes []*ImportFix + for _, imp := range p.existingImports { + // We deliberately ignore globals here, because we can't be sure + // they're in the same package. People do things like put multiple + // main packages in the same directory, and we don't want to + // remove imports if they happen to have the same name as a var in + // a different package. + if _, ok := p.allRefs[p.importIdentifier(imp)]; !ok { + fixes = append(fixes, &ImportFix{ + StmtInfo: *imp, + IdentName: p.importIdentifier(imp), + FixType: DeleteImport, + }) + continue + } + + // An existing import may need to update its import name to be correct. + if name := p.importSpecName(imp); name != imp.Name { + fixes = append(fixes, &ImportFix{ + StmtInfo: ImportInfo{ + Name: name, + ImportPath: imp.ImportPath, + }, + IdentName: p.importIdentifier(imp), + FixType: SetImportName, + }) + } + } + + for _, imp := range selected { + fixes = append(fixes, &ImportFix{ + StmtInfo: ImportInfo{ + Name: p.importSpecName(imp), + ImportPath: imp.ImportPath, + }, + IdentName: p.importIdentifier(imp), + FixType: AddImport, + }) + } + + return fixes, true +} + +// importSpecName gets the import name of imp in the import spec. +// +// When the import identifier matches the assumed import name, the import name does +// not appear in the import spec. +func (p *pass) importSpecName(imp *ImportInfo) string { + // If we did not load the real package names, or the name is already set, + // we just return the existing name. + if !p.loadRealPackageNames || imp.Name != "" { + return imp.Name + } + + ident := p.importIdentifier(imp) + if ident == ImportPathToAssumedName(imp.ImportPath) { + return "" // ident not needed since the assumed and real names are the same. + } + return ident +} + +// apply will perform the fixes on f in order. +func apply(fset *token.FileSet, f *ast.File, fixes []*ImportFix) { + for _, fix := range fixes { + switch fix.FixType { + case DeleteImport: + astutil.DeleteNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath) + case AddImport: + astutil.AddNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath) + case SetImportName: + // Find the matching import path and change the name. + for _, spec := range f.Imports { + path := strings.Trim(spec.Path.Value, `"`) + if path == fix.StmtInfo.ImportPath { + spec.Name = &ast.Ident{ + Name: fix.StmtInfo.Name, + NamePos: spec.Pos(), + } + } + } + } + } +} + +// assumeSiblingImportsValid assumes that siblings' use of packages is valid, +// adding the exports they use. +func (p *pass) assumeSiblingImportsValid() { + for _, f := range p.otherFiles { + refs := collectReferences(f) + imports := collectImports(f) + importsByName := map[string]*ImportInfo{} + for _, imp := range imports { + importsByName[p.importIdentifier(imp)] = imp + } + for left, rights := range refs { + if imp, ok := importsByName[left]; ok { + if m, ok := stdlib[imp.ImportPath]; ok { + // We have the stdlib in memory; no need to guess. + rights = copyExports(m) + } + p.addCandidate(imp, &packageInfo{ + // no name; we already know it. + exports: rights, + }) + } + } + } +} + +// addCandidate adds a candidate import to p, and merges in the information +// in pkg. +func (p *pass) addCandidate(imp *ImportInfo, pkg *packageInfo) { + p.candidates = append(p.candidates, imp) + if existing, ok := p.knownPackages[imp.ImportPath]; ok { + if existing.name == "" { + existing.name = pkg.name + } + for export := range pkg.exports { + existing.exports[export] = true + } + } else { + p.knownPackages[imp.ImportPath] = pkg + } +} + +// fixImports adds and removes imports from f so that all its references are +// satisfied and there are no unused imports. +// +// This is declared as a variable rather than a function so goimports can +// easily be extended by adding a file with an init function. +var fixImports = fixImportsDefault + +func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) error { + fixes, err := getFixes(fset, f, filename, env) + if err != nil { + return err + } + apply(fset, f, fixes) + return err +} + +// getFixes gets the import fixes that need to be made to f in order to fix the imports. +// It does not modify the ast. +func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*ImportFix, error) { + abs, err := filepath.Abs(filename) + if err != nil { + return nil, err + } + srcDir := filepath.Dir(abs) + if env.Logf != nil { + env.Logf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir) + } + + // First pass: looking only at f, and using the naive algorithm to + // derive package names from import paths, see if the file is already + // complete. We can't add any imports yet, because we don't know + // if missing references are actually package vars. + p := &pass{fset: fset, f: f, srcDir: srcDir, env: env} + if fixes, done := p.load(); done { + return fixes, nil + } + + otherFiles := parseOtherFiles(fset, srcDir, filename) + + // Second pass: add information from other files in the same package, + // like their package vars and imports. + p.otherFiles = otherFiles + if fixes, done := p.load(); done { + return fixes, nil + } + + // Now we can try adding imports from the stdlib. + p.assumeSiblingImportsValid() + addStdlibCandidates(p, p.missingRefs) + if fixes, done := p.fix(); done { + return fixes, nil + } + + // Third pass: get real package names where we had previously used + // the naive algorithm. + p = &pass{fset: fset, f: f, srcDir: srcDir, env: env} + p.loadRealPackageNames = true + p.otherFiles = otherFiles + if fixes, done := p.load(); done { + return fixes, nil + } + + if err := addStdlibCandidates(p, p.missingRefs); err != nil { + return nil, err + } + p.assumeSiblingImportsValid() + if fixes, done := p.fix(); done { + return fixes, nil + } + + // Go look for candidates in $GOPATH, etc. We don't necessarily load + // the real exports of sibling imports, so keep assuming their contents. + if err := addExternalCandidates(p, p.missingRefs, filename); err != nil { + return nil, err + } + + p.lastTry = true + fixes, _ := p.fix() + return fixes, nil +} + +// MaxRelevance is the highest relevance, used for the standard library. +// Chosen arbitrarily to match pre-existing gopls code. +const MaxRelevance = 7.0 + +// getCandidatePkgs works with the passed callback to find all acceptable packages. +// It deduplicates by import path, and uses a cached stdlib rather than reading +// from disk. +func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filename, filePkg string, env *ProcessEnv) error { + notSelf := func(p *pkg) bool { + return p.packageName != filePkg || p.dir != filepath.Dir(filename) + } + goenv, err := env.goEnv() + if err != nil { + return err + } + + var mu sync.Mutex // to guard asynchronous access to dupCheck + dupCheck := map[string]struct{}{} + + // Start off with the standard library. + for importPath, exports := range stdlib { + p := &pkg{ + dir: filepath.Join(goenv["GOROOT"], "src", importPath), + importPathShort: importPath, + packageName: path.Base(importPath), + relevance: MaxRelevance, + } + dupCheck[importPath] = struct{}{} + if notSelf(p) && wrappedCallback.dirFound(p) && wrappedCallback.packageNameLoaded(p) { + wrappedCallback.exportsLoaded(p, exports) + } + } + + scanFilter := &scanCallback{ + rootFound: func(root gopathwalk.Root) bool { + // Exclude goroot results -- getting them is relatively expensive, not cached, + // and generally redundant with the in-memory version. + return root.Type != gopathwalk.RootGOROOT && wrappedCallback.rootFound(root) + }, + dirFound: wrappedCallback.dirFound, + packageNameLoaded: func(pkg *pkg) bool { + mu.Lock() + defer mu.Unlock() + if _, ok := dupCheck[pkg.importPathShort]; ok { + return false + } + dupCheck[pkg.importPathShort] = struct{}{} + return notSelf(pkg) && wrappedCallback.packageNameLoaded(pkg) + }, + exportsLoaded: func(pkg *pkg, exports []string) { + // If we're an x_test, load the package under test's test variant. + if strings.HasSuffix(filePkg, "_test") && pkg.dir == filepath.Dir(filename) { + var err error + _, exports, err = loadExportsFromFiles(ctx, env, pkg.dir, true) + if err != nil { + return + } + } + wrappedCallback.exportsLoaded(pkg, exports) + }, + } + resolver, err := env.GetResolver() + if err != nil { + return err + } + return resolver.scan(ctx, scanFilter) +} + +func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map[string]float64, error) { + result := make(map[string]float64) + resolver, err := env.GetResolver() + if err != nil { + return nil, err + } + for _, path := range paths { + result[path] = resolver.scoreImportPath(ctx, path) + } + return result, nil +} + +func PrimeCache(ctx context.Context, env *ProcessEnv) error { + // Fully scan the disk for directories, but don't actually read any Go files. + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + return false + }, + packageNameLoaded: func(pkg *pkg) bool { + return false + }, + } + return getCandidatePkgs(ctx, callback, "", "", env) +} + +func candidateImportName(pkg *pkg) string { + if ImportPathToAssumedName(pkg.importPathShort) != pkg.packageName { + return pkg.packageName + } + return "" +} + +// GetAllCandidates calls wrapped for each package whose name starts with +// searchPrefix, and can be imported from filename with the package name filePkg. +func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error { + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + if !canUse(filename, pkg.dir) { + return false + } + // Try the assumed package name first, then a simpler path match + // in case of packages named vN, which are not uncommon. + return strings.HasPrefix(ImportPathToAssumedName(pkg.importPathShort), searchPrefix) || + strings.HasPrefix(path.Base(pkg.importPathShort), searchPrefix) + }, + packageNameLoaded: func(pkg *pkg) bool { + if !strings.HasPrefix(pkg.packageName, searchPrefix) { + return false + } + wrapped(ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: pkg.importPathShort, + Name: candidateImportName(pkg), + }, + IdentName: pkg.packageName, + FixType: AddImport, + Relevance: pkg.relevance, + }) + return false + }, + } + return getCandidatePkgs(ctx, callback, filename, filePkg, env) +} + +// GetImportPaths calls wrapped for each package whose import path starts with +// searchPrefix, and can be imported from filename with the package name filePkg. +func GetImportPaths(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error { + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + if !canUse(filename, pkg.dir) { + return false + } + return strings.HasPrefix(pkg.importPathShort, searchPrefix) + }, + packageNameLoaded: func(pkg *pkg) bool { + wrapped(ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: pkg.importPathShort, + Name: candidateImportName(pkg), + }, + IdentName: pkg.packageName, + FixType: AddImport, + Relevance: pkg.relevance, + }) + return false + }, + } + return getCandidatePkgs(ctx, callback, filename, filePkg, env) +} + +// A PackageExport is a package and its exports. +type PackageExport struct { + Fix *ImportFix + Exports []string +} + +// GetPackageExports returns all known packages with name pkg and their exports. +func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error { + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true + }, + dirFound: func(pkg *pkg) bool { + return pkgIsCandidate(filename, references{searchPkg: nil}, pkg) + }, + packageNameLoaded: func(pkg *pkg) bool { + return pkg.packageName == searchPkg + }, + exportsLoaded: func(pkg *pkg, exports []string) { + sort.Strings(exports) + wrapped(PackageExport{ + Fix: &ImportFix{ + StmtInfo: ImportInfo{ + ImportPath: pkg.importPathShort, + Name: candidateImportName(pkg), + }, + IdentName: pkg.packageName, + FixType: AddImport, + Relevance: pkg.relevance, + }, + Exports: exports, + }) + }, + } + return getCandidatePkgs(ctx, callback, filename, filePkg, env) +} + +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 +// the go command, the go/build package, etc. +type ProcessEnv struct { + GocmdRunner *gocommand.Runner + + BuildFlags []string + ModFlag string + ModFile string + + // SkipPathInScan returns true if the path should be skipped from scans of + // the RootCurrentModule root type. The function argument is a clean, + // absolute path. + SkipPathInScan func(string) bool + + // Env overrides the OS environment, and can be used to specify + // GOPROXY, GO111MODULE, etc. PATH cannot be set here, because + // exec.Command will not honor it. + // Specifying all of RequiredGoEnvVars avoids a call to `go env`. + Env map[string]string + + WorkingDir string + + // If Logf is non-nil, debug logging is enabled through this function. + Logf func(format string, args ...interface{}) + + initialized bool + + resolver Resolver +} + +func (e *ProcessEnv) goEnv() (map[string]string, error) { + if err := e.init(); err != nil { + return nil, err + } + return e.Env, nil +} + +func (e *ProcessEnv) matchFile(dir, name string) (bool, error) { + bctx, err := e.buildContext() + if err != nil { + return false, err + } + return bctx.MatchFile(dir, name) +} + +// CopyConfig copies the env's configuration into a new env. +func (e *ProcessEnv) CopyConfig() *ProcessEnv { + copy := &ProcessEnv{ + GocmdRunner: e.GocmdRunner, + initialized: e.initialized, + BuildFlags: e.BuildFlags, + Logf: e.Logf, + WorkingDir: e.WorkingDir, + resolver: nil, + Env: map[string]string{}, + } + for k, v := range e.Env { + copy.Env[k] = v + } + return copy +} + +func (e *ProcessEnv) init() error { + if e.initialized { + return nil + } + + foundAllRequired := true + for _, k := range RequiredGoEnvVars { + if _, ok := e.Env[k]; !ok { + foundAllRequired = false + break + } + } + if foundAllRequired { + e.initialized = true + return nil + } + + if e.Env == nil { + e.Env = map[string]string{} + } + + goEnv := map[string]string{} + stdout, err := e.invokeGo(context.TODO(), "env", append([]string{"-json"}, RequiredGoEnvVars...)...) + if err != nil { + return err + } + if err := json.Unmarshal(stdout.Bytes(), &goEnv); err != nil { + return err + } + for k, v := range goEnv { + e.Env[k] = v + } + e.initialized = true + return nil +} + +func (e *ProcessEnv) env() []string { + var env []string // the gocommand package will prepend os.Environ. + for k, v := range e.Env { + env = append(env, k+"="+v) + } + return env +} + +func (e *ProcessEnv) GetResolver() (Resolver, error) { + if e.resolver != nil { + return e.resolver, nil + } + if err := e.init(); err != nil { + return nil, err + } + if len(e.Env["GOMOD"]) == 0 && len(e.Env["GOWORK"]) == 0 { + e.resolver = newGopathResolver(e) + return e.resolver, nil + } + e.resolver = newModuleResolver(e) + return e.resolver, nil +} + +func (e *ProcessEnv) buildContext() (*build.Context, error) { + ctx := build.Default + goenv, err := e.goEnv() + if err != nil { + return nil, err + } + ctx.GOROOT = goenv["GOROOT"] + ctx.GOPATH = goenv["GOPATH"] + + // As of Go 1.14, build.Context has a Dir field + // (see golang.org/issue/34860). + // Populate it only if present. + rc := reflect.ValueOf(&ctx).Elem() + dir := rc.FieldByName("Dir") + if dir.IsValid() && dir.Kind() == reflect.String { + dir.SetString(e.WorkingDir) + } + + // Since Go 1.11, go/build.Context.Import may invoke 'go list' depending on + // the value in GO111MODULE in the process's environment. We always want to + // run in GOPATH mode when calling Import, so we need to prevent this from + // happening. In Go 1.16, GO111MODULE defaults to "on", so this problem comes + // up more frequently. + // + // HACK: setting any of the Context I/O hooks prevents Import from invoking + // 'go list', regardless of GO111MODULE. This is undocumented, but it's + // unlikely to change before GOPATH support is removed. + ctx.ReadDir = ioutil.ReadDir + + return &ctx, nil +} + +func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string) (*bytes.Buffer, error) { + inv := gocommand.Invocation{ + Verb: verb, + Args: args, + BuildFlags: e.BuildFlags, + Env: e.env(), + Logf: e.Logf, + WorkingDir: e.WorkingDir, + } + return e.GocmdRunner.Run(ctx, inv) +} + +func addStdlibCandidates(pass *pass, refs references) error { + goenv, err := pass.env.goEnv() + if err != nil { + return err + } + add := func(pkg string) { + // Prevent self-imports. + if path.Base(pkg) == pass.f.Name.Name && filepath.Join(goenv["GOROOT"], "src", pkg) == pass.srcDir { + return + } + exports := copyExports(stdlib[pkg]) + pass.addCandidate( + &ImportInfo{ImportPath: pkg}, + &packageInfo{name: path.Base(pkg), exports: exports}) + } + for left := range refs { + if left == "rand" { + // Make sure we try crypto/rand before math/rand. + add("crypto/rand") + add("math/rand") + continue + } + for importPath := range stdlib { + if path.Base(importPath) == left { + add(importPath) + } + } + } + return nil +} + +// A Resolver does the build-system-specific parts of goimports. +type Resolver interface { + // loadPackageNames loads the package names in importPaths. + loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) + // scan works with callback to search for packages. See scanCallback for details. + scan(ctx context.Context, callback *scanCallback) error + // loadExports returns the set of exported symbols in the package at dir. + // loadExports may be called concurrently. + loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) + // scoreImportPath returns the relevance for an import path. + scoreImportPath(ctx context.Context, path string) float64 + + ClearForNewScan() +} + +// A scanCallback controls a call to scan and receives its results. +// In general, minor errors will be silently discarded; a user should not +// expect to receive a full series of calls for everything. +type scanCallback struct { + // rootFound is called before scanning a new root dir. If it returns true, + // the root will be scanned. Returning false will not necessarily prevent + // directories from that root making it to dirFound. + rootFound func(gopathwalk.Root) bool + // dirFound is called when a directory is found that is possibly a Go package. + // pkg will be populated with everything except packageName. + // If it returns true, the package's name will be loaded. + dirFound func(pkg *pkg) bool + // packageNameLoaded is called when a package is found and its name is loaded. + // If it returns true, the package's exports will be loaded. + packageNameLoaded func(pkg *pkg) bool + // exportsLoaded is called when a package's exports have been loaded. + exportsLoaded func(pkg *pkg, exports []string) +} + +func addExternalCandidates(pass *pass, refs references, filename string) error { + var mu sync.Mutex + found := make(map[string][]pkgDistance) + callback := &scanCallback{ + rootFound: func(gopathwalk.Root) bool { + return true // We want everything. + }, + dirFound: func(pkg *pkg) bool { + return pkgIsCandidate(filename, refs, pkg) + }, + packageNameLoaded: func(pkg *pkg) bool { + if _, want := refs[pkg.packageName]; !want { + return false + } + if pkg.dir == pass.srcDir && pass.f.Name.Name == pkg.packageName { + // The candidate is in the same directory and has the + // same package name. Don't try to import ourselves. + return false + } + if !canUse(filename, pkg.dir) { + return false + } + mu.Lock() + defer mu.Unlock() + found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(pass.srcDir, pkg.dir)}) + return false // We'll do our own loading after we sort. + }, + } + resolver, err := pass.env.GetResolver() + if err != nil { + return err + } + if err = resolver.scan(context.Background(), callback); err != nil { + return err + } + + // Search for imports matching potential package references. + type result struct { + imp *ImportInfo + pkg *packageInfo + } + results := make(chan result, len(refs)) + + ctx, cancel := context.WithCancel(context.TODO()) + var wg sync.WaitGroup + defer func() { + cancel() + wg.Wait() + }() + var ( + firstErr error + firstErrOnce sync.Once + ) + for pkgName, symbols := range refs { + wg.Add(1) + go func(pkgName string, symbols map[string]bool) { + defer wg.Done() + + found, err := findImport(ctx, pass, found[pkgName], pkgName, symbols, filename) + + if err != nil { + firstErrOnce.Do(func() { + firstErr = err + cancel() + }) + return + } + + if found == nil { + return // No matching package. + } + + imp := &ImportInfo{ + ImportPath: found.importPathShort, + } + + pkg := &packageInfo{ + name: pkgName, + exports: symbols, + } + results <- result{imp, pkg} + }(pkgName, symbols) + } + go func() { + wg.Wait() + close(results) + }() + + for result := range results { + pass.addCandidate(result.imp, result.pkg) + } + return firstErr +} + +// notIdentifier reports whether ch is an invalid identifier character. +func notIdentifier(ch rune) bool { + return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || + '0' <= ch && ch <= '9' || + ch == '_' || + ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch))) +} + +// ImportPathToAssumedName returns the assumed package name of an import path. +// It does this using only string parsing of the import path. +// It picks the last element of the path that does not look like a major +// version, and then picks the valid identifier off the start of that element. +// It is used to determine if a local rename should be added to an import for +// clarity. +// This function could be moved to a standard package and exported if we want +// for use in other tools. +func ImportPathToAssumedName(importPath string) string { + base := path.Base(importPath) + if strings.HasPrefix(base, "v") { + if _, err := strconv.Atoi(base[1:]); err == nil { + dir := path.Dir(importPath) + if dir != "." { + base = path.Base(dir) + } + } + } + base = strings.TrimPrefix(base, "go-") + if i := strings.IndexFunc(base, notIdentifier); i >= 0 { + base = base[:i] + } + return base +} + +// gopathResolver implements resolver for GOPATH workspaces. +type gopathResolver struct { + env *ProcessEnv + walked bool + cache *dirInfoCache + scanSema chan struct{} // scanSema prevents concurrent scans. +} + +func newGopathResolver(env *ProcessEnv) *gopathResolver { + r := &gopathResolver{ + env: env, + cache: &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + }, + scanSema: make(chan struct{}, 1), + } + r.scanSema <- struct{}{} + return r +} + +func (r *gopathResolver) ClearForNewScan() { + <-r.scanSema + 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) { + names := map[string]string{} + bctx, err := r.env.buildContext() + if err != nil { + return nil, err + } + for _, path := range importPaths { + names[path] = importPathToName(bctx, path, srcDir) + } + return names, nil +} + +// importPathToName finds out the actual package name, as declared in its .go files. +func importPathToName(bctx *build.Context, importPath, srcDir string) string { + // Fast path for standard library without going to disk. + if _, ok := stdlib[importPath]; ok { + return path.Base(importPath) // stdlib packages always match their paths. + } + + buildPkg, err := bctx.Import(importPath, srcDir, build.FindOnly) + if err != nil { + return "" + } + pkgName, err := packageDirToName(buildPkg.Dir) + if err != nil { + return "" + } + return pkgName +} + +// packageDirToName is a faster version of build.Import if +// the only thing desired is the package name. Given a directory, +// packageDirToName then only parses one file in the package, +// trusting that the files in the directory are consistent. +func packageDirToName(dir string) (packageName string, err error) { + d, err := os.Open(dir) + if err != nil { + return "", err + } + names, err := d.Readdirnames(-1) + d.Close() + if err != nil { + return "", err + } + sort.Strings(names) // to have predictable behavior + var lastErr error + var nfile int + for _, name := range names { + if !strings.HasSuffix(name, ".go") { + continue + } + if strings.HasSuffix(name, "_test.go") { + continue + } + nfile++ + fullFile := filepath.Join(dir, name) + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly) + if err != nil { + lastErr = err + continue + } + pkgName := f.Name.Name + if pkgName == "documentation" { + // Special case from go/build.ImportDir, not + // handled by ctx.MatchFile. + continue + } + if pkgName == "main" { + // Also skip package main, assuming it's a +build ignore generator or example. + // Since you can't import a package main anyway, there's no harm here. + continue + } + return pkgName, nil + } + if lastErr != nil { + return "", lastErr + } + return "", fmt.Errorf("no importable package found in %d Go files", nfile) +} + +type pkg struct { + dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http") + importPathShort string // vendorless import path ("net/http", "a/b") + packageName string // package name loaded from source if requested + relevance float64 // a weakly-defined score of how relevant a package is. 0 is most relevant. +} + +type pkgDistance struct { + pkg *pkg + distance int // relative distance to target +} + +// byDistanceOrImportPathShortLength sorts by relative distance breaking ties +// on the short import path length and then the import string itself. +type byDistanceOrImportPathShortLength []pkgDistance + +func (s byDistanceOrImportPathShortLength) Len() int { return len(s) } +func (s byDistanceOrImportPathShortLength) Less(i, j int) bool { + di, dj := s[i].distance, s[j].distance + if di == -1 { + return false + } + if dj == -1 { + return true + } + if di != dj { + return di < dj + } + + vi, vj := s[i].pkg.importPathShort, s[j].pkg.importPathShort + if len(vi) != len(vj) { + return len(vi) < len(vj) + } + return vi < vj +} +func (s byDistanceOrImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func distance(basepath, targetpath string) int { + p, err := filepath.Rel(basepath, targetpath) + if err != nil { + return -1 + } + if p == "." { + return 0 + } + return strings.Count(p, string(filepath.Separator)) + 1 +} + +func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error { + add := func(root gopathwalk.Root, dir string) { + // We assume cached directories have not changed. We can skip them and their + // children. + if _, ok := r.cache.Load(dir); ok { + return + } + + importpath := filepath.ToSlash(dir[len(root.Path)+len("/"):]) + info := directoryPackageInfo{ + status: directoryScanned, + dir: dir, + rootType: root.Type, + nonCanonicalImportPath: VendorlessPath(importpath), + } + r.cache.Store(dir, info) + } + processDir := func(info directoryPackageInfo) { + // Skip this directory if we were not able to get the package information successfully. + if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { + return + } + + p := &pkg{ + importPathShort: info.nonCanonicalImportPath, + dir: info.dir, + relevance: MaxRelevance - 1, + } + if info.rootType == gopathwalk.RootGOROOT { + p.relevance = MaxRelevance + } + + if !callback.dirFound(p) { + return + } + var err error + p.packageName, err = r.cache.CachePackageName(info) + if err != nil { + return + } + + if !callback.packageNameLoaded(p) { + return + } + if _, exports, err := r.loadExports(ctx, p, false); err == nil { + callback.exportsLoaded(p, exports) + } + } + stop := r.cache.ScanAndListen(ctx, processDir) + defer stop() + + goenv, err := r.env.goEnv() + if err != nil { + return err + } + var roots []gopathwalk.Root + roots = append(roots, gopathwalk.Root{Path: filepath.Join(goenv["GOROOT"], "src"), Type: gopathwalk.RootGOROOT}) + for _, p := range filepath.SplitList(goenv["GOPATH"]) { + roots = append(roots, gopathwalk.Root{Path: filepath.Join(p, "src"), Type: gopathwalk.RootGOPATH}) + } + // The callback is not necessarily safe to use in the goroutine below. Process roots eagerly. + roots = filterRoots(roots, callback.rootFound) + // We can't cancel walks, because we need them to finish to have a usable + // cache. Instead, run them in a separate goroutine and detach. + scanDone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + return + case <-r.scanSema: + } + defer func() { r.scanSema <- struct{}{} }() + gopathwalk.Walk(roots, add, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: false}) + close(scanDone) + }() + select { + case <-ctx.Done(): + case <-scanDone: + } + return nil +} + +func (r *gopathResolver) scoreImportPath(ctx context.Context, path string) float64 { + if _, ok := stdlib[path]; ok { + return MaxRelevance + } + return MaxRelevance - 1 +} + +func filterRoots(roots []gopathwalk.Root, include func(gopathwalk.Root) bool) []gopathwalk.Root { + var result []gopathwalk.Root + for _, root := range roots { + if !include(root) { + continue + } + result = append(result, root) + } + return result +} + +func (r *gopathResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) { + if info, ok := r.cache.Load(pkg.dir); ok && !includeTest { + return r.cache.CacheExports(ctx, r.env, info) + } + return loadExportsFromFiles(ctx, r.env, pkg.dir, includeTest) +} + +// VendorlessPath returns the devendorized version of the import path ipath. +// For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b". +func VendorlessPath(ipath string) string { + // Devendorize for use in import statement. + if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { + return ipath[i+len("/vendor/"):] + } + if strings.HasPrefix(ipath, "vendor/") { + return ipath[len("vendor/"):] + } + return ipath +} + +func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) { + // Look for non-test, buildable .go files which could provide exports. + all, err := ioutil.ReadDir(dir) + if err != nil { + return "", nil, err + } + var files []os.FileInfo + for _, fi := range all { + name := fi.Name() + if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) { + continue + } + match, err := env.matchFile(dir, fi.Name()) + if err != nil || !match { + continue + } + files = append(files, fi) + } + + if len(files) == 0 { + return "", nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", dir) + } + + var pkgName string + var exports []string + fset := token.NewFileSet() + for _, fi := range files { + select { + case <-ctx.Done(): + return "", nil, ctx.Err() + default: + } + + fullFile := filepath.Join(dir, fi.Name()) + f, err := parser.ParseFile(fset, fullFile, nil, 0) + if err != nil { + if env.Logf != nil { + env.Logf("error parsing %v: %v", fullFile, err) + } + continue + } + if f.Name.Name == "documentation" { + // Special case from go/build.ImportDir, not + // handled by MatchFile above. + continue + } + if includeTest && strings.HasSuffix(f.Name.Name, "_test") { + // x_test package. We want internal test files only. + continue + } + pkgName = f.Name.Name + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports = append(exports, name) + } + } + } + + if env.Logf != nil { + sortedExports := append([]string(nil), exports...) + sort.Strings(sortedExports) + env.Logf("loaded exports in dir %v (package %v): %v", dir, pkgName, strings.Join(sortedExports, ", ")) + } + return pkgName, exports, nil +} + +// findImport searches for a package with the given symbols. +// 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) { + // Sort the candidates by their import package length, + // assuming that shorter package names are better than long + // ones. Note that this sorts by the de-vendored name, so + // there's no "penalty" for vendoring. + sort.Sort(byDistanceOrImportPathShortLength(candidates)) + if pass.env.Logf != nil { + for i, c := range candidates { + pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir) + } + } + resolver, err := pass.env.GetResolver() + if err != nil { + return nil, err + } + + // Collect exports for packages with matching names. + rescv := make([]chan *pkg, len(candidates)) + for i := range candidates { + rescv[i] = make(chan *pkg, 1) + } + const maxConcurrentPackageImport = 4 + loadExportsSem := make(chan struct{}, maxConcurrentPackageImport) + + ctx, cancel := context.WithCancel(ctx) + var wg sync.WaitGroup + defer func() { + cancel() + wg.Wait() + }() + + wg.Add(1) + go func() { + defer wg.Done() + for i, c := range candidates { + select { + case loadExportsSem <- struct{}{}: + case <-ctx.Done(): + return + } + + wg.Add(1) + go func(c pkgDistance, resc chan<- *pkg) { + defer func() { + <-loadExportsSem + wg.Done() + }() + + if pass.env.Logf != nil { + pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName) + } + // If we're an x_test, load the package under test's test variant. + includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir + _, exports, err := resolver.loadExports(ctx, c.pkg, includeTest) + if err != nil { + if pass.env.Logf != nil { + pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err) + } + resc <- nil + return + } + + exportsMap := make(map[string]bool, len(exports)) + for _, sym := range exports { + exportsMap[sym] = true + } + + // If it doesn't have the right + // symbols, send nil to mean no match. + for symbol := range symbols { + if !exportsMap[symbol] { + resc <- nil + return + } + } + resc <- c.pkg + }(c, rescv[i]) + } + }() + + for _, resc := range rescv { + pkg := <-resc + if pkg == nil { + continue + } + return pkg, nil + } + return nil, nil +} + +// pkgIsCandidate reports whether pkg is a candidate for satisfying the +// finding which package pkgIdent in the file named by filename is trying +// to refer to. +// +// This check is purely lexical and is meant to be as fast as possible +// because it's run over all $GOPATH directories to filter out poor +// candidates in order to limit the CPU and I/O later parsing the +// exports in candidate packages. +// +// filename is the file being formatted. +// pkgIdent is the package being searched for, like "client" (if +// searching for "client.New") +func pkgIsCandidate(filename string, refs references, pkg *pkg) bool { + // Check "internal" and "vendor" visibility: + if !canUse(filename, pkg.dir) { + return false + } + + // Speed optimization to minimize disk I/O: + // the last two components on disk must contain the + // package name somewhere. + // + // This permits mismatch naming like directory + // "go-foo" being package "foo", or "pkg.v3" being "pkg", + // or directory "google.golang.org/api/cloudbilling/v1" + // being package "cloudbilling", but doesn't + // permit a directory "foo" to be package + // "bar", which is strongly discouraged + // anyway. There's no reason goimports needs + // to be slow just to accommodate that. + for pkgIdent := range refs { + lastTwo := lastTwoComponents(pkg.importPathShort) + if strings.Contains(lastTwo, pkgIdent) { + return true + } + if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) { + lastTwo = lowerASCIIAndRemoveHyphen(lastTwo) + if strings.Contains(lastTwo, pkgIdent) { + return true + } + } + } + return false +} + +func hasHyphenOrUpperASCII(s string) bool { + for i := 0; i < len(s); i++ { + b := s[i] + if b == '-' || ('A' <= b && b <= 'Z') { + return true + } + } + return false +} + +func lowerASCIIAndRemoveHyphen(s string) (ret string) { + buf := make([]byte, 0, len(s)) + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case b == '-': + continue + case 'A' <= b && b <= 'Z': + buf = append(buf, b+('a'-'A')) + default: + buf = append(buf, b) + } + } + return string(buf) +} + +// canUse reports whether the package in dir is usable from filename, +// respecting the Go "internal" and "vendor" visibility rules. +func canUse(filename, dir string) bool { + // Fast path check, before any allocations. If it doesn't contain vendor + // or internal, it's not tricky: + // Note that this can false-negative on directories like "notinternal", + // but we check it correctly below. This is just a fast path. + if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") { + return true + } + + dirSlash := filepath.ToSlash(dir) + if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") { + return true + } + // Vendor or internal directory only visible from children of parent. + // That means the path from the current directory to the target directory + // can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal + // or bar/vendor or bar/internal. + // After stripping all the leading ../, the only okay place to see vendor or internal + // is at the very beginning of the path. + absfile, err := filepath.Abs(filename) + if err != nil { + return false + } + absdir, err := filepath.Abs(dir) + if err != nil { + return false + } + rel, err := filepath.Rel(absfile, absdir) + if err != nil { + return false + } + relSlash := filepath.ToSlash(rel) + if i := strings.LastIndex(relSlash, "../"); i >= 0 { + relSlash = relSlash[i+len("../"):] + } + return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal") +} + +// lastTwoComponents returns at most the last two path components +// of v, using either / or \ as the path separator. +func lastTwoComponents(v string) string { + nslash := 0 + for i := len(v) - 1; i >= 0; i-- { + if v[i] == '/' || v[i] == '\\' { + nslash++ + if nslash == 2 { + return v[i:] + } + } + } + return v +} + +type visitFn func(node ast.Node) ast.Visitor + +func (fn visitFn) Visit(node ast.Node) ast.Visitor { + return fn(node) +} + +func copyExports(pkg []string) map[string]bool { + m := make(map[string]bool, len(pkg)) + for _, v := range pkg { + m[v] = true + } + return m +} diff --git a/gopls/golang_org_x_tools/imports/imports.go b/gopls/golang_org_x_tools/imports/imports.go new file mode 100644 index 0000000..95a8838 --- /dev/null +++ b/gopls/golang_org_x_tools/imports/imports.go @@ -0,0 +1,351 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run mkstdlib.go + +// Package imports implements a Go pretty-printer (like package "go/format") +// that also adds or removes import statements as necessary. +package imports + +import ( + "bufio" + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/printer" + "go/token" + "io" + "regexp" + "strconv" + "strings" + + "golang.org/x/tools/go/ast/astutil" +) + +// Options is golang.org/x/tools/imports.Options with extra internal-only options. +type Options struct { + Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state. + + // LocalPrefix is a comma-separated string of import path prefixes, which, if + // set, instructs Process to sort the import paths with the given prefixes + // into another group after 3rd-party packages. + LocalPrefix string + + Fragment bool // Accept fragment of a source file (no package statement) + AllErrors bool // Report all errors (not just the first 10 on different lines) + + Comments bool // Print comments (true if nil *Options provided) + TabIndent bool // Use tabs for indent (true if nil *Options provided) + TabWidth int // Tab width (8 if nil *Options provided) + + FormatOnly bool // Disable the insertion and deletion of imports +} + +// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env. +func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) { + fileSet := token.NewFileSet() + file, adjust, err := parse(fileSet, filename, src, opt) + if err != nil { + return nil, err + } + + if !opt.FormatOnly { + if err := fixImports(fileSet, file, filename, opt.Env); err != nil { + return nil, err + } + } + return formatFile(fileSet, file, src, adjust, opt) +} + +// FixImports returns a list of fixes to the imports that, when applied, +// will leave the imports in the same state as Process. src and opt must +// be specified. +// +// Note that filename's directory influences which imports can be chosen, +// so it is important that filename be accurate. +func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) { + fileSet := token.NewFileSet() + file, _, err := parse(fileSet, filename, src, opt) + if err != nil { + return nil, err + } + + return getFixes(fileSet, file, filename, opt.Env) +} + +// ApplyFixes applies all of the fixes to the file and formats it. extraMode +// is added in when parsing the file. src and opts must be specified, but no +// env is needed. +func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) { + // Don't use parse() -- we don't care about fragments or statement lists + // here, and we need to work with unparseable files. + fileSet := token.NewFileSet() + parserMode := parser.Mode(0) + if opt.Comments { + parserMode |= parser.ParseComments + } + if opt.AllErrors { + parserMode |= parser.AllErrors + } + parserMode |= extraMode + + file, err := parser.ParseFile(fileSet, filename, src, parserMode) + if file == nil { + return nil, err + } + + // Apply the fixes to the file. + apply(fileSet, file, fixes) + + return formatFile(fileSet, file, src, nil, opt) +} + +// formatFile formats the file syntax tree. +// It may mutate the token.FileSet. +// +// If an adjust function is provided, it is called after formatting +// with the original source (formatFile's src parameter) and the +// formatted file, and returns the postpocessed result. +func formatFile(fset *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) { + mergeImports(file) + sortImports(opt.LocalPrefix, fset.File(file.Pos()), file) + var spacesBefore []string // import paths we need spaces before + for _, impSection := range astutil.Imports(fset, file) { + // Within each block of contiguous imports, see if any + // import lines are in different group numbers. If so, + // we'll need to put a space between them so it's + // compatible with gofmt. + lastGroup := -1 + for _, importSpec := range impSection { + importPath, _ := strconv.Unquote(importSpec.Path.Value) + groupNum := importGroup(opt.LocalPrefix, importPath) + if groupNum != lastGroup && lastGroup != -1 { + spacesBefore = append(spacesBefore, importPath) + } + lastGroup = groupNum + } + + } + + printerMode := printer.UseSpaces + if opt.TabIndent { + printerMode |= printer.TabIndent + } + printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth} + + var buf bytes.Buffer + err := printConfig.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + out := buf.Bytes() + if adjust != nil { + out = adjust(src, out) + } + if len(spacesBefore) > 0 { + out, err = addImportSpaces(bytes.NewReader(out), spacesBefore) + if err != nil { + return nil, err + } + } + + out, err = format.Source(out) + if err != nil { + return nil, err + } + return out, nil +} + +// parse parses src, which was read from filename, +// as a Go source file or statement list. +func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) { + parserMode := parser.Mode(0) + if opt.Comments { + parserMode |= parser.ParseComments + } + if opt.AllErrors { + parserMode |= parser.AllErrors + } + + // Try as whole source file. + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err == nil { + return file, nil, nil + } + // If the error is that the source file didn't begin with a + // package line and we accept fragmented input, fall through to + // try as a source fragment. Stop and return on any other error. + if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") { + return nil, nil, err + } + + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ;, not a newline, so that parse errors are on + // the correct line. + const prefix = "package main;" + psrc := append([]byte(prefix), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) + if err == nil { + // Gofmt will turn the ; into a \n. + // Do that ourselves now and update the file contents, + // so that positions and line numbers are correct going forward. + psrc[len(prefix)-1] = '\n' + fset.File(file.Package).SetLinesForContent(psrc) + + // If a main function exists, we will assume this is a main + // package and leave the file. + if containsMainFunc(file) { + return file, nil, nil + } + + adjust := func(orig, src []byte) []byte { + // Remove the package clause. + src = src[len(prefix):] + return matchSpace(orig, src) + } + return file, adjust, nil + } + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.Error(), "expected declaration") { + return nil, nil, err + } + + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ;, not a newline, so that the line numbers + // in fsrc match the ones in src. + fsrc := append(append([]byte("package p; func _() {"), src...), '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) + if err == nil { + adjust := func(orig, src []byte) []byte { + // Remove the wrapping. + // Gofmt has turned the ; into a \n\n. + src = src[len("package p\n\nfunc _() {"):] + src = src[:len(src)-len("}\n")] + // Gofmt has also indented the function body one level. + // Remove that indent. + src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) + return matchSpace(orig, src) + } + return file, adjust, nil + } + + // Failed, and out of options. + return nil, nil, err +} + +// containsMainFunc checks if a file contains a function declaration with the +// function signature 'func main()' +func containsMainFunc(file *ast.File) bool { + for _, decl := range file.Decls { + if f, ok := decl.(*ast.FuncDecl); ok { + if f.Name.Name != "main" { + continue + } + + if len(f.Type.Params.List) != 0 { + continue + } + + if f.Type.Results != nil && len(f.Type.Results.List) != 0 { + continue + } + + return true + } + } + + return false +} + +func cutSpace(b []byte) (before, middle, after []byte) { + i := 0 + for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { + i++ + } + j := len(b) + for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { + j-- + } + if i <= j { + return b[:i], b[i:j], b[j:] + } + return nil, nil, b[j:] +} + +// matchSpace reformats src to use the same space context as orig. +// 1. If orig begins with blank lines, matchSpace inserts them at the beginning of src. +// 2. matchSpace copies the indentation of the first non-blank line in orig +// to every non-blank line in src. +// 3. matchSpace copies the trailing space from orig and uses it in place +// of src's trailing space. +func matchSpace(orig []byte, src []byte) []byte { + before, _, after := cutSpace(orig) + i := bytes.LastIndex(before, []byte{'\n'}) + before, indent := before[:i+1], before[i+1:] + + _, src, _ = cutSpace(src) + + var b bytes.Buffer + b.Write(before) + for len(src) > 0 { + line := src + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, src = line[:i+1], line[i+1:] + } else { + src = nil + } + if len(line) > 0 && line[0] != '\n' { // not blank + b.Write(indent) + } + b.Write(line) + } + b.Write(after) + return b.Bytes() +} + +var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+?)"`) + +func addImportSpaces(r io.Reader, breaks []string) ([]byte, error) { + var out bytes.Buffer + in := bufio.NewReader(r) + inImports := false + done := false + for { + s, err := in.ReadString('\n') + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + + if !inImports && !done && strings.HasPrefix(s, "import") { + inImports = true + } + if inImports && (strings.HasPrefix(s, "var") || + strings.HasPrefix(s, "func") || + strings.HasPrefix(s, "const") || + strings.HasPrefix(s, "type")) { + done = true + inImports = false + } + if inImports && len(breaks) > 0 { + if m := impLine.FindStringSubmatch(s); m != nil { + if m[1] == breaks[0] { + out.WriteByte('\n') + breaks = breaks[1:] + } + } + } + + fmt.Fprint(&out, s) + } + return out.Bytes(), nil +} diff --git a/gopls/golang_org_x_tools/imports/mod.go b/gopls/golang_org_x_tools/imports/mod.go new file mode 100644 index 0000000..1e2e1b4 --- /dev/null +++ b/gopls/golang_org_x_tools/imports/mod.go @@ -0,0 +1,716 @@ +// 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 imports + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + + "golang.org/x/mod/module" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gocommand" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gopathwalk" +) + +// ModuleResolver implements resolver for modules using the go command as little +// as feasible. +type ModuleResolver struct { + 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 + mains []*gocommand.ModuleJSON + mainByDir map[string]*gocommand.ModuleJSON + modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path... + modsByDir []*gocommand.ModuleJSON // ...or Dir. + + // moduleCacheCache stores information about the module cache. + moduleCacheCache *dirInfoCache + otherCache *dirInfoCache +} + +func newModuleResolver(e *ProcessEnv) *ModuleResolver { + r := &ModuleResolver{ + env: e, + scanSema: make(chan struct{}, 1), + } + r.scanSema <- struct{}{} + return r +} + +func (r *ModuleResolver) init() error { + if r.initialized { + return nil + } + + goenv, err := r.env.goEnv() + if err != nil { + return err + } + inv := gocommand.Invocation{ + BuildFlags: r.env.BuildFlags, + ModFlag: r.env.ModFlag, + ModFile: r.env.ModFile, + Env: r.env.env(), + Logf: r.env.Logf, + WorkingDir: r.env.WorkingDir, + } + + vendorEnabled := false + var mainModVendor *gocommand.ModuleJSON + + // Module vendor directories are ignored in workspace mode: + // https://go.googlesource.com/proposal/+/master/design/45713-workspace.md + if len(r.env.Env["GOWORK"]) == 0 { + vendorEnabled, mainModVendor, err = gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner) + if err != nil { + return err + } + } + + if mainModVendor != nil && vendorEnabled { + // Vendor mode is on, so all the non-Main modules are irrelevant, + // and we need to search /vendor for everything. + r.mains = []*gocommand.ModuleJSON{mainModVendor} + r.dummyVendorMod = &gocommand.ModuleJSON{ + Path: "", + Dir: filepath.Join(mainModVendor.Dir, "vendor"), + } + r.modsByModPath = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod} + r.modsByDir = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod} + } else { + // Vendor mode is off, so run go list -m ... to find everything. + err := r.initAllMods() + // We expect an error when running outside of a module with + // GO111MODULE=on. Other errors are fatal. + 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") { + return err + } + } + } + + if gmc := r.env.Env["GOMODCACHE"]; gmc != "" { + r.moduleCacheDir = gmc + } else { + 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 { + count := func(x int) int { + return strings.Count(r.modsByModPath[x].Path, "/") + } + return count(j) < count(i) // descending order + }) + sort.Slice(r.modsByDir, func(i, j int) bool { + count := func(x int) int { + return strings.Count(r.modsByDir[x].Dir, "/") + } + return count(j) < count(i) // descending order + }) + + r.roots = []gopathwalk.Root{ + {Path: filepath.Join(goenv["GOROOT"], "/src"), Type: gopathwalk.RootGOROOT}, + } + r.mainByDir = make(map[string]*gocommand.ModuleJSON) + for _, main := range r.mains { + r.roots = append(r.roots, gopathwalk.Root{Path: main.Dir, Type: gopathwalk.RootCurrentModule}) + r.mainByDir[main.Dir] = main + } + if vendorEnabled { + r.roots = append(r.roots, gopathwalk.Root{Path: r.dummyVendorMod.Dir, Type: gopathwalk.RootOther}) + } else { + addDep := func(mod *gocommand.ModuleJSON) { + if mod.Replace == nil { + // This is redundant with the cache, but we'll skip it cheaply enough. + r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootModuleCache}) + } else { + r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootOther}) + } + } + // Walk dependent modules before scanning the full mod cache, direct deps first. + for _, mod := range r.modsByModPath { + if !mod.Indirect && !mod.Main { + addDep(mod) + } + } + for _, mod := range r.modsByModPath { + if mod.Indirect && !mod.Main { + addDep(mod) + } + } + r.roots = append(r.roots, gopathwalk.Root{Path: r.moduleCacheDir, Type: gopathwalk.RootModuleCache}) + } + + r.scannedRoots = map[gopathwalk.Root]bool{} + if r.moduleCacheCache == nil { + r.moduleCacheCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + } + } + if r.otherCache == nil { + r.otherCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + } + } + r.initialized = true + return nil +} + +func (r *ModuleResolver) initAllMods() error { + stdout, err := r.env.invokeGo(context.TODO(), "list", "-m", "-e", "-json", "...") + if err != nil { + return err + } + for dec := json.NewDecoder(stdout); dec.More(); { + mod := &gocommand.ModuleJSON{} + if err := dec.Decode(mod); err != nil { + return err + } + if mod.Dir == "" { + if r.env.Logf != nil { + r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path) + } + // Can't do anything with a module that's not downloaded. + continue + } + // golang/go#36193: the go command doesn't always clean paths. + mod.Dir = filepath.Clean(mod.Dir) + r.modsByModPath = append(r.modsByModPath, mod) + r.modsByDir = append(r.modsByDir, mod) + if mod.Main { + r.mains = append(r.mains, mod) + } + } + return nil +} + +func (r *ModuleResolver) ClearForNewScan() { + <-r.scanSema + r.scannedRoots = map[gopathwalk.Root]bool{} + r.otherCache = &dirInfoCache{ + dirs: map[string]*directoryPackageInfo{}, + listeners: map[*int]cacheListener{}, + } + r.scanSema <- struct{}{} +} + +func (r *ModuleResolver) ClearForNewMod() { + <-r.scanSema + *r = ModuleResolver{ + env: r.env, + moduleCacheCache: r.moduleCacheCache, + otherCache: r.otherCache, + scanSema: r.scanSema, + } + r.init() + r.scanSema <- struct{}{} +} + +// findPackage returns the module and directory 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) { + // This can't find packages in the stdlib, but that's harmless for all + // the existing code paths. + for _, m := range r.modsByModPath { + if !strings.HasPrefix(importPath, m.Path) { + continue + } + pathInModule := importPath[len(m.Path):] + pkgDir := filepath.Join(m.Dir, pathInModule) + if r.dirIsNestedModule(pkgDir, m) { + continue + } + + if info, ok := r.cacheLoad(pkgDir); ok { + if loaded, err := info.reachedStatus(nameLoaded); loaded { + if err != nil { + continue // No package in this dir. + } + return m, pkgDir + } + if scanned, err := info.reachedStatus(directoryScanned); scanned && err != nil { + continue // Dir is unreadable, etc. + } + // This is slightly wrong: a directory doesn't have to have an + // importable package to count as a package for package-to-module + // resolution. package main or _test files should count but + // don't. + // TODO(heschi): fix this. + if _, err := r.cachePackageName(info); err == nil { + return m, pkgDir + } + } + + // Not cached. Read the filesystem. + pkgFiles, err := ioutil.ReadDir(pkgDir) + if err != nil { + continue + } + // A module only contains a package if it has buildable go + // files in that directory. If not, it could be provided by an + // outer module. See #29736. + for _, fi := range pkgFiles { + if ok, _ := r.env.matchFile(pkgDir, fi.Name()); ok { + return m, pkgDir + } + } + } + return nil, "" +} + +func (r *ModuleResolver) cacheLoad(dir string) (directoryPackageInfo, bool) { + if info, ok := r.moduleCacheCache.Load(dir); ok { + return info, ok + } + return r.otherCache.Load(dir) +} + +func (r *ModuleResolver) cacheStore(info directoryPackageInfo) { + if info.rootType == gopathwalk.RootModuleCache { + r.moduleCacheCache.Store(info.dir, info) + } else { + r.otherCache.Store(info.dir, info) + } +} + +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. +func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, error) { + if info.rootType == gopathwalk.RootModuleCache { + return r.moduleCacheCache.CachePackageName(info) + } + return r.otherCache.CachePackageName(info) +} + +func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) { + if info.rootType == gopathwalk.RootModuleCache { + return r.moduleCacheCache.CacheExports(ctx, env, info) + } + return r.otherCache.CacheExports(ctx, env, info) +} + +// findModuleByDir returns the module that contains dir, or nil if no such +// module is in scope. +func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON { + // This is quite tricky and may not be correct. dir could be: + // - a package in the main module. + // - a replace target underneath the main module's directory. + // - a nested module in the above. + // - a replace target somewhere totally random. + // - a nested module in the above. + // - in the mod cache. + // - in /vendor/ in -mod=vendor mode. + // - nested module? Dunno. + // Rumor has it that replace targets cannot contain other replace targets. + for _, m := range r.modsByDir { + if !strings.HasPrefix(dir, m.Dir) { + continue + } + + if r.dirIsNestedModule(dir, m) { + continue + } + + return m + } + return nil +} + +// dirIsNestedModule reports if dir is contained in a nested module underneath +// mod, not actually in mod. +func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON) bool { + if !strings.HasPrefix(dir, mod.Dir) { + return false + } + if r.dirInModuleCache(dir) { + // Nested modules in the module cache are pruned, + // so it cannot be a nested module. + return false + } + if mod != nil && mod == r.dummyVendorMod { + // The /vendor pseudomodule is flattened and doesn't actually count. + return false + } + modDir, _ := r.modInfo(dir) + if modDir == "" { + return false + } + return modDir != mod.Dir +} + +func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) { + readModName := func(modFile string) string { + modBytes, err := ioutil.ReadFile(modFile) + if err != nil { + return "" + } + return modulePath(modBytes) + } + + if r.dirInModuleCache(dir) { + if matches := modCacheRegexp.FindStringSubmatch(dir); len(matches) == 3 { + index := strings.Index(dir, matches[1]+"@"+matches[2]) + modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2]) + return modDir, readModName(filepath.Join(modDir, "go.mod")) + } + } + for { + if info, ok := r.cacheLoad(dir); ok { + return info.moduleDir, info.moduleName + } + f := filepath.Join(dir, "go.mod") + info, err := os.Stat(f) + if err == nil && !info.IsDir() { + return dir, readModName(f) + } + + d := filepath.Dir(dir) + if len(d) >= len(dir) { + return "", "" // reached top of file system, no go.mod + } + dir = d + } +} + +func (r *ModuleResolver) dirInModuleCache(dir string) bool { + if r.moduleCacheDir == "" { + return false + } + return strings.HasPrefix(dir, r.moduleCacheDir) +} + +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{} + for _, path := range importPaths { + _, packageDir := r.findPackage(path) + if packageDir == "" { + continue + } + name, err := packageDirToName(packageDir) + if err != nil { + continue + } + names[path] = name + } + return names, nil +} + +func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error { + if err := r.init(); err != nil { + return err + } + + processDir := func(info directoryPackageInfo) { + // Skip this directory if we were not able to get the package information successfully. + if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { + return + } + pkg, err := r.canonicalize(info) + if err != nil { + return + } + + if !callback.dirFound(pkg) { + return + } + pkg.packageName, err = r.cachePackageName(info) + if err != nil { + return + } + + if !callback.packageNameLoaded(pkg) { + return + } + _, exports, err := r.loadExports(ctx, pkg, false) + if err != nil { + return + } + callback.exportsLoaded(pkg, exports) + } + + // Start processing everything in the cache, and listen for the new stuff + // we discover in the walk below. + stop1 := r.moduleCacheCache.ScanAndListen(ctx, processDir) + defer stop1() + stop2 := r.otherCache.ScanAndListen(ctx, processDir) + defer stop2() + + // We assume cached directories are fully cached, including all their + // children, and have not changed. We can skip them. + skip := func(root gopathwalk.Root, dir string) bool { + if r.env.SkipPathInScan != nil && root.Type == gopathwalk.RootCurrentModule { + if root.Path == dir { + return false + } + + if r.env.SkipPathInScan(filepath.Clean(dir)) { + return true + } + } + + info, ok := r.cacheLoad(dir) + if !ok { + return false + } + // This directory can be skipped as long as we have already scanned it. + // Packages with errors will continue to have errors, so there is no need + // to rescan them. + packageScanned, _ := info.reachedStatus(directoryScanned) + return packageScanned + } + + // Add anything new to the cache, and process it if we're still listening. + add := func(root gopathwalk.Root, dir string) { + r.cacheStore(r.scanDirForPackage(root, dir)) + } + + // r.roots and the callback are not necessarily safe to use in the + // goroutine below. Process them eagerly. + roots := filterRoots(r.roots, callback.rootFound) + // We can't cancel walks, because we need them to finish to have a usable + // cache. Instead, run them in a separate goroutine and detach. + scanDone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + return + case <-r.scanSema: + } + defer func() { r.scanSema <- struct{}{} }() + // We have the lock on r.scannedRoots, and no other scans can run. + for _, root := range roots { + if ctx.Err() != nil { + return + } + + if r.scannedRoots[root] { + continue + } + gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: true}) + r.scannedRoots[root] = true + } + close(scanDone) + }() + select { + case <-ctx.Done(): + case <-scanDone: + } + return nil +} + +func (r *ModuleResolver) scoreImportPath(ctx context.Context, path string) float64 { + if _, ok := stdlib[path]; ok { + return MaxRelevance + } + mod, _ := r.findPackage(path) + return modRelevance(mod) +} + +func modRelevance(mod *gocommand.ModuleJSON) float64 { + var relevance float64 + switch { + case mod == nil: // out of scope + return MaxRelevance - 4 + case mod.Indirect: + relevance = MaxRelevance - 3 + case !mod.Main: + relevance = MaxRelevance - 2 + default: + relevance = MaxRelevance - 1 // main module ties with stdlib + } + + _, versionString, ok := module.SplitPathVersion(mod.Path) + if ok { + index := strings.Index(versionString, "v") + if index == -1 { + return relevance + } + if versionNumber, err := strconv.ParseFloat(versionString[index+1:], 64); err == nil { + relevance += versionNumber / 1000 + } + } + + return relevance +} + +// canonicalize gets the result of canonicalizing the packages using the results +// of initializing the resolver from 'go list -m'. +func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) { + // Packages in GOROOT are already canonical, regardless of the std/cmd modules. + if info.rootType == gopathwalk.RootGOROOT { + return &pkg{ + importPathShort: info.nonCanonicalImportPath, + dir: info.dir, + packageName: path.Base(info.nonCanonicalImportPath), + relevance: MaxRelevance, + }, nil + } + + importPath := info.nonCanonicalImportPath + mod := r.findModuleByDir(info.dir) + // Check if the directory is underneath a module that's in scope. + if mod != nil { + // It is. If dir is the target of a replace directive, + // our guessed import path is wrong. Use the real one. + if mod.Dir == info.dir { + importPath = mod.Path + } else { + dirInMod := info.dir[len(mod.Dir)+len("/"):] + importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod)) + } + } else if !strings.HasPrefix(importPath, info.moduleName) { + // The module's name doesn't match the package's import path. It + // probably needs a replace directive we don't have. + return nil, fmt.Errorf("package in %q is not valid without a replace statement", info.dir) + } + + res := &pkg{ + importPathShort: importPath, + dir: info.dir, + relevance: modRelevance(mod), + } + // We may have discovered a package that has a different version + // in scope already. Canonicalize to that one if possible. + if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" { + res.dir = canonicalDir + } + return res, nil +} + +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 { + return r.cacheExports(ctx, r.env, info) + } + return loadExportsFromFiles(ctx, r.env, pkg.dir, includeTest) +} + +func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo { + subdir := "" + if dir != root.Path { + subdir = dir[len(root.Path)+len("/"):] + } + importPath := filepath.ToSlash(subdir) + if strings.HasPrefix(importPath, "vendor/") { + // Only enter vendor directories if they're explicitly requested as a root. + return directoryPackageInfo{ + status: directoryScanned, + err: fmt.Errorf("unwanted vendor directory"), + } + } + switch root.Type { + case gopathwalk.RootCurrentModule: + importPath = path.Join(r.mainByDir[root.Path].Path, filepath.ToSlash(subdir)) + case gopathwalk.RootModuleCache: + 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 r.env.Logf != nil { + r.env.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])) + } + + modDir, modName := r.modInfo(dir) + result := directoryPackageInfo{ + status: directoryScanned, + dir: dir, + rootType: root.Type, + nonCanonicalImportPath: importPath, + moduleDir: modDir, + moduleName: modName, + } + if root.Type == gopathwalk.RootGOROOT { + // stdlib packages are always in scope, despite the confusing go.mod + return result + } + return result +} + +// modCacheRegexp splits a path in a module cache into module, module version, and package. +var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) + +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// modulePath returns the module path from the gomod file text. +// If it cannot find a module path, it returns an empty string. +// It is tolerant of unrelated problems in the go.mod file. +// +// Copied from cmd/go/internal/modfile. +func modulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} diff --git a/gopls/golang_org_x_tools/imports/mod_cache.go b/gopls/golang_org_x_tools/imports/mod_cache.go new file mode 100644 index 0000000..69b88cb --- /dev/null +++ b/gopls/golang_org_x_tools/imports/mod_cache.go @@ -0,0 +1,236 @@ +// 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 imports + +import ( + "context" + "fmt" + "sync" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gopathwalk" +) + +// To find packages to import, the resolver needs to know about all of the +// the packages that could be imported. This includes packages that are +// already in modules that are in (1) the current module, (2) replace targets, +// and (3) packages in the module cache. Packages in (1) and (2) may change over +// time, as the client may edit the current module and locally replaced modules. +// The module cache (which includes all of the packages in (3)) can only +// ever be added to. +// +// The resolver can thus save state about packages in the module cache +// and guarantee that this will not change over time. To obtain information +// about new modules added to the module cache, the module cache should be +// rescanned. +// +// It is OK to serve information about modules that have been deleted, +// as they do still exist. +// TODO(suzmue): can we share information with the caller about +// what module needs to be downloaded to import this package? + +type directoryPackageStatus int + +const ( + _ directoryPackageStatus = iota + directoryScanned + nameLoaded + exportsLoaded +) + +type directoryPackageInfo struct { + // status indicates the extent to which this struct has been filled in. + status directoryPackageStatus + // err is non-nil when there was an error trying to reach status. + err error + + // Set when status >= directoryScanned. + + // dir is the absolute directory of this package. + dir string + rootType gopathwalk.RootType + // nonCanonicalImportPath is the package's expected import path. It may + // not actually be importable at that path. + nonCanonicalImportPath string + + // Module-related information. + moduleDir string // The directory that is the module root of this dir. + moduleName string // The module name that contains this dir. + + // Set when status >= nameLoaded. + + packageName string // the package name, as declared in the source. + + // Set when status >= exportsLoaded. + + exports []string +} + +// reachedStatus returns true when info has a status at least target and any error associated with +// an attempt to reach target. +func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (bool, error) { + if info.err == nil { + return info.status >= target, nil + } + if info.status == target { + return true, info.err + } + return true, nil +} + +// dirInfoCache is a concurrency safe map for storing information about +// directories that may contain packages. +// +// The information in this cache is built incrementally. Entries are initialized in scan. +// No new keys should be added in any other functions, as all directories containing +// packages are identified in scan. +// +// Other functions, including loadExports and findPackage, may update entries in this cache +// as they discover new things about the directory. +// +// 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 +// take care not to hold the cache across changes to the underlying files. +// +// TODO(suzmue): consider other concurrency strategies and data structures (RWLocks, sync.Map, etc) +type dirInfoCache struct { + mu sync.Mutex + // dirs stores information about packages in directories, keyed by absolute path. + dirs map[string]*directoryPackageInfo + listeners map[*int]cacheListener +} + +type cacheListener func(directoryPackageInfo) + +// 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 +// finish and blocks new ones. +func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener) func() { + ctx, cancel := context.WithCancel(ctx) + + // Flushing out all the callbacks is tricky without knowing how many there + // are going to be. Setting an arbitrary limit makes it much easier. + const maxInFlight = 10 + sema := make(chan struct{}, maxInFlight) + for i := 0; i < maxInFlight; i++ { + sema <- struct{}{} + } + + cookie := new(int) // A unique ID we can use for the listener. + + // We can't hold mu while calling the listener. + d.mu.Lock() + var keys []string + for key := range d.dirs { + keys = append(keys, key) + } + d.listeners[cookie] = func(info directoryPackageInfo) { + select { + case <-ctx.Done(): + return + case <-sema: + } + listener(info) + sema <- struct{}{} + } + d.mu.Unlock() + + stop := func() { + cancel() + d.mu.Lock() + delete(d.listeners, cookie) + d.mu.Unlock() + for i := 0; i < maxInFlight; i++ { + <-sema + } + } + + // Process the pre-existing keys. + for _, k := range keys { + select { + case <-ctx.Done(): + return stop + default: + } + if v, ok := d.Load(k); ok { + listener(v) + } + } + + return stop +} + +// Store stores the package info for dir. +func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) { + d.mu.Lock() + _, old := d.dirs[dir] + d.dirs[dir] = &info + var listeners []cacheListener + for _, l := range d.listeners { + listeners = append(listeners, l) + } + d.mu.Unlock() + + if !old { + for _, l := range listeners { + l(info) + } + } +} + +// Load returns a copy of the directoryPackageInfo for absolute directory dir. +func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) { + d.mu.Lock() + defer d.mu.Unlock() + info, ok := d.dirs[dir] + if !ok { + return directoryPackageInfo{}, false + } + return *info, true +} + +// Keys returns the keys currently present in d. +func (d *dirInfoCache) Keys() (keys []string) { + d.mu.Lock() + defer d.mu.Unlock() + for key := range d.dirs { + keys = append(keys, key) + } + return keys +} + +func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (string, error) { + if loaded, err := info.reachedStatus(nameLoaded); loaded { + return info.packageName, err + } + if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { + return "", fmt.Errorf("cannot read package name, scan error: %v", err) + } + info.packageName, info.err = packageDirToName(info.dir) + info.status = nameLoaded + d.Store(info.dir, info) + return info.packageName, info.err +} + +func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) { + if reached, _ := info.reachedStatus(exportsLoaded); reached { + return info.packageName, info.exports, info.err + } + if reached, err := info.reachedStatus(nameLoaded); reached && err != nil { + return "", nil, err + } + info.packageName, info.exports, info.err = loadExportsFromFiles(ctx, env, info.dir, false) + if info.err == context.Canceled || info.err == context.DeadlineExceeded { + return info.packageName, info.exports, info.err + } + // The cache structure wants things to proceed linearly. We can skip a + // step here, but only if we succeed. + if info.status == nameLoaded || info.err == nil { + info.status = exportsLoaded + } else { + info.status = nameLoaded + } + d.Store(info.dir, info) + return info.packageName, info.exports, info.err +} diff --git a/gopls/golang_org_x_tools/imports/sortimports.go b/gopls/golang_org_x_tools/imports/sortimports.go new file mode 100644 index 0000000..85144db --- /dev/null +++ b/gopls/golang_org_x_tools/imports/sortimports.go @@ -0,0 +1,296 @@ +// Copyright 2013 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. + +// Hacked up copy of go/ast/import.go +// Modified to use a single token.File in preference to a FileSet. + +package imports + +import ( + "go/ast" + "go/token" + "log" + "sort" + "strconv" +) + +// sortImports sorts runs of consecutive import lines in import blocks in f. +// It also removes duplicate imports when it is possible to do so without data loss. +// +// It may mutate the token.File. +func sortImports(localPrefix string, tokFile *token.File, f *ast.File) { + for i, d := range f.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.IMPORT { + // Not an import declaration, so we're done. + // Imports are always first. + break + } + + if len(d.Specs) == 0 { + // Empty import block, remove it. + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + } + + if !d.Lparen.IsValid() { + // Not a block: sorted by default. + continue + } + + // Identify and sort runs of specs on successive lines. + i := 0 + specs := d.Specs[:0] + for j, s := range d.Specs { + if j > i && tokFile.Line(s.Pos()) > 1+tokFile.Line(d.Specs[j-1].End()) { + // j begins a new run. End this one. + specs = append(specs, sortSpecs(localPrefix, tokFile, f, d.Specs[i:j])...) + i = j + } + } + specs = append(specs, sortSpecs(localPrefix, tokFile, f, d.Specs[i:])...) + d.Specs = specs + + // Deduping can leave a blank line before the rparen; clean that up. + if len(d.Specs) > 0 { + lastSpec := d.Specs[len(d.Specs)-1] + lastLine := tokFile.PositionFor(lastSpec.Pos(), false).Line + if rParenLine := tokFile.PositionFor(d.Rparen, false).Line; rParenLine > lastLine+1 { + tokFile.MergeLine(rParenLine - 1) // has side effects! + } + } + } +} + +// mergeImports merges all the import declarations into the first one. +// Taken from golang.org/x/tools/ast/astutil. +// This does not adjust line numbers properly +func mergeImports(f *ast.File) { + if len(f.Decls) <= 1 { + return + } + + // Merge all the import declarations into the first one. + var first *ast.GenDecl + for i := 0; i < len(f.Decls); i++ { + decl := f.Decls[i] + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") { + continue + } + if first == nil { + first = gen + continue // Don't touch the first one. + } + // We now know there is more than one package in this import + // declaration. Ensure that it ends up parenthesized. + first.Lparen = first.Pos() + // Move the imports of the other import declaration to the first one. + for _, spec := range gen.Specs { + spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() + first.Specs = append(first.Specs, spec) + } + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + } +} + +// declImports reports whether gen contains an import of path. +// Taken from golang.org/x/tools/ast/astutil. +func declImports(gen *ast.GenDecl, path string) bool { + if gen.Tok != token.IMPORT { + return false + } + for _, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if importPath(impspec) == path { + return true + } + } + return false +} + +func importPath(s ast.Spec) string { + t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value) + if err == nil { + return t + } + return "" +} + +func importName(s ast.Spec) string { + n := s.(*ast.ImportSpec).Name + if n == nil { + return "" + } + return n.Name +} + +func importComment(s ast.Spec) string { + c := s.(*ast.ImportSpec).Comment + if c == nil { + return "" + } + return c.Text() +} + +// collapse indicates whether prev may be removed, leaving only next. +func collapse(prev, next ast.Spec) bool { + if importPath(next) != importPath(prev) || importName(next) != importName(prev) { + return false + } + return prev.(*ast.ImportSpec).Comment == nil +} + +type posSpan struct { + Start token.Pos + End token.Pos +} + +// sortSpecs sorts the import specs within each import decl. +// It may mutate the token.File. +func sortSpecs(localPrefix string, tokFile *token.File, f *ast.File, specs []ast.Spec) []ast.Spec { + // Can't short-circuit here even if specs are already sorted, + // since they might yet need deduplication. + // A lone import, however, may be safely ignored. + if len(specs) <= 1 { + return specs + } + + // Record positions for specs. + pos := make([]posSpan, len(specs)) + for i, s := range specs { + pos[i] = posSpan{s.Pos(), s.End()} + } + + // Identify comments in this range. + // Any comment from pos[0].Start to the final line counts. + lastLine := tokFile.Line(pos[len(pos)-1].End) + cstart := len(f.Comments) + cend := len(f.Comments) + for i, g := range f.Comments { + if g.Pos() < pos[0].Start { + continue + } + if i < cstart { + cstart = i + } + if tokFile.Line(g.End()) > lastLine { + cend = i + break + } + } + comments := f.Comments[cstart:cend] + + // Assign each comment to the import spec preceding it. + importComment := map[*ast.ImportSpec][]*ast.CommentGroup{} + specIndex := 0 + for _, g := range comments { + for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() { + specIndex++ + } + s := specs[specIndex].(*ast.ImportSpec) + importComment[s] = append(importComment[s], g) + } + + // Sort the import specs by import path. + // Remove duplicates, when possible without data loss. + // Reassign the import paths to have the same position sequence. + // Reassign each comment to abut the end of its spec. + // Sort the comments by new position. + sort.Sort(byImportSpec{localPrefix, specs}) + + // Dedup. Thanks to our sorting, we can just consider + // adjacent pairs of imports. + deduped := specs[:0] + for i, s := range specs { + if i == len(specs)-1 || !collapse(s, specs[i+1]) { + deduped = append(deduped, s) + } else { + p := s.Pos() + tokFile.MergeLine(tokFile.Line(p)) // has side effects! + } + } + specs = deduped + + // Fix up comment positions + for i, s := range specs { + s := s.(*ast.ImportSpec) + if s.Name != nil { + s.Name.NamePos = pos[i].Start + } + s.Path.ValuePos = pos[i].Start + s.EndPos = pos[i].End + nextSpecPos := pos[i].End + + for _, g := range importComment[s] { + for _, c := range g.List { + c.Slash = pos[i].End + nextSpecPos = c.End() + } + } + if i < len(specs)-1 { + pos[i+1].Start = nextSpecPos + pos[i+1].End = nextSpecPos + } + } + + sort.Sort(byCommentPos(comments)) + + // Fixup comments can insert blank lines, because import specs are on different lines. + // We remove those blank lines here by merging import spec to the first import spec line. + firstSpecLine := tokFile.Line(specs[0].Pos()) + for _, s := range specs[1:] { + p := s.Pos() + line := tokFile.Line(p) + for previousLine := line - 1; previousLine >= firstSpecLine; { + // MergeLine can panic. Avoid the panic at the cost of not removing the blank line + // golang/go#50329 + if previousLine > 0 && previousLine < tokFile.LineCount() { + tokFile.MergeLine(previousLine) // has side effects! + previousLine-- + } else { + // try to gather some data to diagnose how this could happen + req := "Please report what the imports section of your go file looked like." + log.Printf("panic avoided: first:%d line:%d previous:%d max:%d. %s", + firstSpecLine, line, previousLine, tokFile.LineCount(), req) + } + } + } + return specs +} + +type byImportSpec struct { + localPrefix string + specs []ast.Spec // slice of *ast.ImportSpec +} + +func (x byImportSpec) Len() int { return len(x.specs) } +func (x byImportSpec) Swap(i, j int) { x.specs[i], x.specs[j] = x.specs[j], x.specs[i] } +func (x byImportSpec) Less(i, j int) bool { + ipath := importPath(x.specs[i]) + jpath := importPath(x.specs[j]) + + igroup := importGroup(x.localPrefix, ipath) + jgroup := importGroup(x.localPrefix, jpath) + if igroup != jgroup { + return igroup < jgroup + } + + if ipath != jpath { + return ipath < jpath + } + iname := importName(x.specs[i]) + jname := importName(x.specs[j]) + + if iname != jname { + return iname < jname + } + return importComment(x.specs[i]) < importComment(x.specs[j]) +} + +type byCommentPos []*ast.CommentGroup + +func (x byCommentPos) Len() int { return len(x) } +func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() } diff --git a/gopls/golang_org_x_tools/imports/zstdlib.go b/gopls/golang_org_x_tools/imports/zstdlib.go new file mode 100644 index 0000000..5db9b2d --- /dev/null +++ b/gopls/golang_org_x_tools/imports/zstdlib.go @@ -0,0 +1,10929 @@ +// 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. + +// Code generated by mkstdlib.go. DO NOT EDIT. + +package imports + +var stdlib = map[string][]string{ + "archive/tar": { + "ErrFieldTooLong", + "ErrHeader", + "ErrWriteAfterClose", + "ErrWriteTooLong", + "FileInfoHeader", + "Format", + "FormatGNU", + "FormatPAX", + "FormatUSTAR", + "FormatUnknown", + "Header", + "NewReader", + "NewWriter", + "Reader", + "TypeBlock", + "TypeChar", + "TypeCont", + "TypeDir", + "TypeFifo", + "TypeGNULongLink", + "TypeGNULongName", + "TypeGNUSparse", + "TypeLink", + "TypeReg", + "TypeRegA", + "TypeSymlink", + "TypeXGlobalHeader", + "TypeXHeader", + "Writer", + }, + "archive/zip": { + "Compressor", + "Decompressor", + "Deflate", + "ErrAlgorithm", + "ErrChecksum", + "ErrFormat", + "File", + "FileHeader", + "FileInfoHeader", + "NewReader", + "NewWriter", + "OpenReader", + "ReadCloser", + "Reader", + "RegisterCompressor", + "RegisterDecompressor", + "Store", + "Writer", + }, + "bufio": { + "ErrAdvanceTooFar", + "ErrBadReadCount", + "ErrBufferFull", + "ErrFinalToken", + "ErrInvalidUnreadByte", + "ErrInvalidUnreadRune", + "ErrNegativeAdvance", + "ErrNegativeCount", + "ErrTooLong", + "MaxScanTokenSize", + "NewReadWriter", + "NewReader", + "NewReaderSize", + "NewScanner", + "NewWriter", + "NewWriterSize", + "ReadWriter", + "Reader", + "ScanBytes", + "ScanLines", + "ScanRunes", + "ScanWords", + "Scanner", + "SplitFunc", + "Writer", + }, + "bytes": { + "Buffer", + "Compare", + "Contains", + "ContainsAny", + "ContainsRune", + "Count", + "Cut", + "Equal", + "EqualFold", + "ErrTooLarge", + "Fields", + "FieldsFunc", + "HasPrefix", + "HasSuffix", + "Index", + "IndexAny", + "IndexByte", + "IndexFunc", + "IndexRune", + "Join", + "LastIndex", + "LastIndexAny", + "LastIndexByte", + "LastIndexFunc", + "Map", + "MinRead", + "NewBuffer", + "NewBufferString", + "NewReader", + "Reader", + "Repeat", + "Replace", + "ReplaceAll", + "Runes", + "Split", + "SplitAfter", + "SplitAfterN", + "SplitN", + "Title", + "ToLower", + "ToLowerSpecial", + "ToTitle", + "ToTitleSpecial", + "ToUpper", + "ToUpperSpecial", + "ToValidUTF8", + "Trim", + "TrimFunc", + "TrimLeft", + "TrimLeftFunc", + "TrimPrefix", + "TrimRight", + "TrimRightFunc", + "TrimSpace", + "TrimSuffix", + }, + "compress/bzip2": { + "NewReader", + "StructuralError", + }, + "compress/flate": { + "BestCompression", + "BestSpeed", + "CorruptInputError", + "DefaultCompression", + "HuffmanOnly", + "InternalError", + "NewReader", + "NewReaderDict", + "NewWriter", + "NewWriterDict", + "NoCompression", + "ReadError", + "Reader", + "Resetter", + "WriteError", + "Writer", + }, + "compress/gzip": { + "BestCompression", + "BestSpeed", + "DefaultCompression", + "ErrChecksum", + "ErrHeader", + "Header", + "HuffmanOnly", + "NewReader", + "NewWriter", + "NewWriterLevel", + "NoCompression", + "Reader", + "Writer", + }, + "compress/lzw": { + "LSB", + "MSB", + "NewReader", + "NewWriter", + "Order", + "Reader", + "Writer", + }, + "compress/zlib": { + "BestCompression", + "BestSpeed", + "DefaultCompression", + "ErrChecksum", + "ErrDictionary", + "ErrHeader", + "HuffmanOnly", + "NewReader", + "NewReaderDict", + "NewWriter", + "NewWriterLevel", + "NewWriterLevelDict", + "NoCompression", + "Resetter", + "Writer", + }, + "container/heap": { + "Fix", + "Init", + "Interface", + "Pop", + "Push", + "Remove", + }, + "container/list": { + "Element", + "List", + "New", + }, + "container/ring": { + "New", + "Ring", + }, + "context": { + "Background", + "CancelFunc", + "Canceled", + "Context", + "DeadlineExceeded", + "TODO", + "WithCancel", + "WithDeadline", + "WithTimeout", + "WithValue", + }, + "crypto": { + "BLAKE2b_256", + "BLAKE2b_384", + "BLAKE2b_512", + "BLAKE2s_256", + "Decrypter", + "DecrypterOpts", + "Hash", + "MD4", + "MD5", + "MD5SHA1", + "PrivateKey", + "PublicKey", + "RIPEMD160", + "RegisterHash", + "SHA1", + "SHA224", + "SHA256", + "SHA384", + "SHA3_224", + "SHA3_256", + "SHA3_384", + "SHA3_512", + "SHA512", + "SHA512_224", + "SHA512_256", + "Signer", + "SignerOpts", + }, + "crypto/aes": { + "BlockSize", + "KeySizeError", + "NewCipher", + }, + "crypto/cipher": { + "AEAD", + "Block", + "BlockMode", + "NewCBCDecrypter", + "NewCBCEncrypter", + "NewCFBDecrypter", + "NewCFBEncrypter", + "NewCTR", + "NewGCM", + "NewGCMWithNonceSize", + "NewGCMWithTagSize", + "NewOFB", + "Stream", + "StreamReader", + "StreamWriter", + }, + "crypto/des": { + "BlockSize", + "KeySizeError", + "NewCipher", + "NewTripleDESCipher", + }, + "crypto/dsa": { + "ErrInvalidPublicKey", + "GenerateKey", + "GenerateParameters", + "L1024N160", + "L2048N224", + "L2048N256", + "L3072N256", + "ParameterSizes", + "Parameters", + "PrivateKey", + "PublicKey", + "Sign", + "Verify", + }, + "crypto/ecdsa": { + "GenerateKey", + "PrivateKey", + "PublicKey", + "Sign", + "SignASN1", + "Verify", + "VerifyASN1", + }, + "crypto/ed25519": { + "GenerateKey", + "NewKeyFromSeed", + "PrivateKey", + "PrivateKeySize", + "PublicKey", + "PublicKeySize", + "SeedSize", + "Sign", + "SignatureSize", + "Verify", + }, + "crypto/elliptic": { + "Curve", + "CurveParams", + "GenerateKey", + "Marshal", + "MarshalCompressed", + "P224", + "P256", + "P384", + "P521", + "Unmarshal", + "UnmarshalCompressed", + }, + "crypto/hmac": { + "Equal", + "New", + }, + "crypto/md5": { + "BlockSize", + "New", + "Size", + "Sum", + }, + "crypto/rand": { + "Int", + "Prime", + "Read", + "Reader", + }, + "crypto/rc4": { + "Cipher", + "KeySizeError", + "NewCipher", + }, + "crypto/rsa": { + "CRTValue", + "DecryptOAEP", + "DecryptPKCS1v15", + "DecryptPKCS1v15SessionKey", + "EncryptOAEP", + "EncryptPKCS1v15", + "ErrDecryption", + "ErrMessageTooLong", + "ErrVerification", + "GenerateKey", + "GenerateMultiPrimeKey", + "OAEPOptions", + "PKCS1v15DecryptOptions", + "PSSOptions", + "PSSSaltLengthAuto", + "PSSSaltLengthEqualsHash", + "PrecomputedValues", + "PrivateKey", + "PublicKey", + "SignPKCS1v15", + "SignPSS", + "VerifyPKCS1v15", + "VerifyPSS", + }, + "crypto/sha1": { + "BlockSize", + "New", + "Size", + "Sum", + }, + "crypto/sha256": { + "BlockSize", + "New", + "New224", + "Size", + "Size224", + "Sum224", + "Sum256", + }, + "crypto/sha512": { + "BlockSize", + "New", + "New384", + "New512_224", + "New512_256", + "Size", + "Size224", + "Size256", + "Size384", + "Sum384", + "Sum512", + "Sum512_224", + "Sum512_256", + }, + "crypto/subtle": { + "ConstantTimeByteEq", + "ConstantTimeCompare", + "ConstantTimeCopy", + "ConstantTimeEq", + "ConstantTimeLessOrEq", + "ConstantTimeSelect", + }, + "crypto/tls": { + "Certificate", + "CertificateRequestInfo", + "CipherSuite", + "CipherSuiteName", + "CipherSuites", + "Client", + "ClientAuthType", + "ClientHelloInfo", + "ClientSessionCache", + "ClientSessionState", + "Config", + "Conn", + "ConnectionState", + "CurveID", + "CurveP256", + "CurveP384", + "CurveP521", + "Dial", + "DialWithDialer", + "Dialer", + "ECDSAWithP256AndSHA256", + "ECDSAWithP384AndSHA384", + "ECDSAWithP521AndSHA512", + "ECDSAWithSHA1", + "Ed25519", + "InsecureCipherSuites", + "Listen", + "LoadX509KeyPair", + "NewLRUClientSessionCache", + "NewListener", + "NoClientCert", + "PKCS1WithSHA1", + "PKCS1WithSHA256", + "PKCS1WithSHA384", + "PKCS1WithSHA512", + "PSSWithSHA256", + "PSSWithSHA384", + "PSSWithSHA512", + "RecordHeaderError", + "RenegotiateFreelyAsClient", + "RenegotiateNever", + "RenegotiateOnceAsClient", + "RenegotiationSupport", + "RequestClientCert", + "RequireAndVerifyClientCert", + "RequireAnyClientCert", + "Server", + "SignatureScheme", + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + "TLS_FALLBACK_SCSV", + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_RC4_128_SHA", + "VerifyClientCertIfGiven", + "VersionSSL30", + "VersionTLS10", + "VersionTLS11", + "VersionTLS12", + "VersionTLS13", + "X25519", + "X509KeyPair", + }, + "crypto/x509": { + "CANotAuthorizedForExtKeyUsage", + "CANotAuthorizedForThisName", + "CertPool", + "Certificate", + "CertificateInvalidError", + "CertificateRequest", + "ConstraintViolationError", + "CreateCertificate", + "CreateCertificateRequest", + "CreateRevocationList", + "DSA", + "DSAWithSHA1", + "DSAWithSHA256", + "DecryptPEMBlock", + "ECDSA", + "ECDSAWithSHA1", + "ECDSAWithSHA256", + "ECDSAWithSHA384", + "ECDSAWithSHA512", + "Ed25519", + "EncryptPEMBlock", + "ErrUnsupportedAlgorithm", + "Expired", + "ExtKeyUsage", + "ExtKeyUsageAny", + "ExtKeyUsageClientAuth", + "ExtKeyUsageCodeSigning", + "ExtKeyUsageEmailProtection", + "ExtKeyUsageIPSECEndSystem", + "ExtKeyUsageIPSECTunnel", + "ExtKeyUsageIPSECUser", + "ExtKeyUsageMicrosoftCommercialCodeSigning", + "ExtKeyUsageMicrosoftKernelCodeSigning", + "ExtKeyUsageMicrosoftServerGatedCrypto", + "ExtKeyUsageNetscapeServerGatedCrypto", + "ExtKeyUsageOCSPSigning", + "ExtKeyUsageServerAuth", + "ExtKeyUsageTimeStamping", + "HostnameError", + "IncompatibleUsage", + "IncorrectPasswordError", + "InsecureAlgorithmError", + "InvalidReason", + "IsEncryptedPEMBlock", + "KeyUsage", + "KeyUsageCRLSign", + "KeyUsageCertSign", + "KeyUsageContentCommitment", + "KeyUsageDataEncipherment", + "KeyUsageDecipherOnly", + "KeyUsageDigitalSignature", + "KeyUsageEncipherOnly", + "KeyUsageKeyAgreement", + "KeyUsageKeyEncipherment", + "MD2WithRSA", + "MD5WithRSA", + "MarshalECPrivateKey", + "MarshalPKCS1PrivateKey", + "MarshalPKCS1PublicKey", + "MarshalPKCS8PrivateKey", + "MarshalPKIXPublicKey", + "NameConstraintsWithoutSANs", + "NameMismatch", + "NewCertPool", + "NotAuthorizedToSign", + "PEMCipher", + "PEMCipher3DES", + "PEMCipherAES128", + "PEMCipherAES192", + "PEMCipherAES256", + "PEMCipherDES", + "ParseCRL", + "ParseCertificate", + "ParseCertificateRequest", + "ParseCertificates", + "ParseDERCRL", + "ParseECPrivateKey", + "ParsePKCS1PrivateKey", + "ParsePKCS1PublicKey", + "ParsePKCS8PrivateKey", + "ParsePKIXPublicKey", + "ParseRevocationList", + "PublicKeyAlgorithm", + "PureEd25519", + "RSA", + "RevocationList", + "SHA1WithRSA", + "SHA256WithRSA", + "SHA256WithRSAPSS", + "SHA384WithRSA", + "SHA384WithRSAPSS", + "SHA512WithRSA", + "SHA512WithRSAPSS", + "SignatureAlgorithm", + "SystemCertPool", + "SystemRootsError", + "TooManyConstraints", + "TooManyIntermediates", + "UnconstrainedName", + "UnhandledCriticalExtension", + "UnknownAuthorityError", + "UnknownPublicKeyAlgorithm", + "UnknownSignatureAlgorithm", + "VerifyOptions", + }, + "crypto/x509/pkix": { + "AlgorithmIdentifier", + "AttributeTypeAndValue", + "AttributeTypeAndValueSET", + "CertificateList", + "Extension", + "Name", + "RDNSequence", + "RelativeDistinguishedNameSET", + "RevokedCertificate", + "TBSCertificateList", + }, + "database/sql": { + "ColumnType", + "Conn", + "DB", + "DBStats", + "Drivers", + "ErrConnDone", + "ErrNoRows", + "ErrTxDone", + "IsolationLevel", + "LevelDefault", + "LevelLinearizable", + "LevelReadCommitted", + "LevelReadUncommitted", + "LevelRepeatableRead", + "LevelSerializable", + "LevelSnapshot", + "LevelWriteCommitted", + "Named", + "NamedArg", + "NullBool", + "NullByte", + "NullFloat64", + "NullInt16", + "NullInt32", + "NullInt64", + "NullString", + "NullTime", + "Open", + "OpenDB", + "Out", + "RawBytes", + "Register", + "Result", + "Row", + "Rows", + "Scanner", + "Stmt", + "Tx", + "TxOptions", + }, + "database/sql/driver": { + "Bool", + "ColumnConverter", + "Conn", + "ConnBeginTx", + "ConnPrepareContext", + "Connector", + "DefaultParameterConverter", + "Driver", + "DriverContext", + "ErrBadConn", + "ErrRemoveArgument", + "ErrSkip", + "Execer", + "ExecerContext", + "Int32", + "IsScanValue", + "IsValue", + "IsolationLevel", + "NamedValue", + "NamedValueChecker", + "NotNull", + "Null", + "Pinger", + "Queryer", + "QueryerContext", + "Result", + "ResultNoRows", + "Rows", + "RowsAffected", + "RowsColumnTypeDatabaseTypeName", + "RowsColumnTypeLength", + "RowsColumnTypeNullable", + "RowsColumnTypePrecisionScale", + "RowsColumnTypeScanType", + "RowsNextResultSet", + "SessionResetter", + "Stmt", + "StmtExecContext", + "StmtQueryContext", + "String", + "Tx", + "TxOptions", + "Validator", + "Value", + "ValueConverter", + "Valuer", + }, + "debug/buildinfo": { + "BuildInfo", + "Read", + "ReadFile", + }, + "debug/dwarf": { + "AddrType", + "ArrayType", + "Attr", + "AttrAbstractOrigin", + "AttrAccessibility", + "AttrAddrBase", + "AttrAddrClass", + "AttrAlignment", + "AttrAllocated", + "AttrArtificial", + "AttrAssociated", + "AttrBaseTypes", + "AttrBinaryScale", + "AttrBitOffset", + "AttrBitSize", + "AttrByteSize", + "AttrCallAllCalls", + "AttrCallAllSourceCalls", + "AttrCallAllTailCalls", + "AttrCallColumn", + "AttrCallDataLocation", + "AttrCallDataValue", + "AttrCallFile", + "AttrCallLine", + "AttrCallOrigin", + "AttrCallPC", + "AttrCallParameter", + "AttrCallReturnPC", + "AttrCallTailCall", + "AttrCallTarget", + "AttrCallTargetClobbered", + "AttrCallValue", + "AttrCalling", + "AttrCommonRef", + "AttrCompDir", + "AttrConstExpr", + "AttrConstValue", + "AttrContainingType", + "AttrCount", + "AttrDataBitOffset", + "AttrDataLocation", + "AttrDataMemberLoc", + "AttrDecimalScale", + "AttrDecimalSign", + "AttrDeclColumn", + "AttrDeclFile", + "AttrDeclLine", + "AttrDeclaration", + "AttrDefaultValue", + "AttrDefaulted", + "AttrDeleted", + "AttrDescription", + "AttrDigitCount", + "AttrDiscr", + "AttrDiscrList", + "AttrDiscrValue", + "AttrDwoName", + "AttrElemental", + "AttrEncoding", + "AttrEndianity", + "AttrEntrypc", + "AttrEnumClass", + "AttrExplicit", + "AttrExportSymbols", + "AttrExtension", + "AttrExternal", + "AttrFrameBase", + "AttrFriend", + "AttrHighpc", + "AttrIdentifierCase", + "AttrImport", + "AttrInline", + "AttrIsOptional", + "AttrLanguage", + "AttrLinkageName", + "AttrLocation", + "AttrLoclistsBase", + "AttrLowerBound", + "AttrLowpc", + "AttrMacroInfo", + "AttrMacros", + "AttrMainSubprogram", + "AttrMutable", + "AttrName", + "AttrNamelistItem", + "AttrNoreturn", + "AttrObjectPointer", + "AttrOrdering", + "AttrPictureString", + "AttrPriority", + "AttrProducer", + "AttrPrototyped", + "AttrPure", + "AttrRanges", + "AttrRank", + "AttrRecursive", + "AttrReference", + "AttrReturnAddr", + "AttrRnglistsBase", + "AttrRvalueReference", + "AttrSegment", + "AttrSibling", + "AttrSignature", + "AttrSmall", + "AttrSpecification", + "AttrStartScope", + "AttrStaticLink", + "AttrStmtList", + "AttrStrOffsetsBase", + "AttrStride", + "AttrStrideSize", + "AttrStringLength", + "AttrStringLengthBitSize", + "AttrStringLengthByteSize", + "AttrThreadsScaled", + "AttrTrampoline", + "AttrType", + "AttrUpperBound", + "AttrUseLocation", + "AttrUseUTF8", + "AttrVarParam", + "AttrVirtuality", + "AttrVisibility", + "AttrVtableElemLoc", + "BasicType", + "BoolType", + "CharType", + "Class", + "ClassAddrPtr", + "ClassAddress", + "ClassBlock", + "ClassConstant", + "ClassExprLoc", + "ClassFlag", + "ClassLinePtr", + "ClassLocList", + "ClassLocListPtr", + "ClassMacPtr", + "ClassRangeListPtr", + "ClassReference", + "ClassReferenceAlt", + "ClassReferenceSig", + "ClassRngList", + "ClassRngListsPtr", + "ClassStrOffsetsPtr", + "ClassString", + "ClassStringAlt", + "ClassUnknown", + "CommonType", + "ComplexType", + "Data", + "DecodeError", + "DotDotDotType", + "Entry", + "EnumType", + "EnumValue", + "ErrUnknownPC", + "Field", + "FloatType", + "FuncType", + "IntType", + "LineEntry", + "LineFile", + "LineReader", + "LineReaderPos", + "New", + "Offset", + "PtrType", + "QualType", + "Reader", + "StructField", + "StructType", + "Tag", + "TagAccessDeclaration", + "TagArrayType", + "TagAtomicType", + "TagBaseType", + "TagCallSite", + "TagCallSiteParameter", + "TagCatchDwarfBlock", + "TagClassType", + "TagCoarrayType", + "TagCommonDwarfBlock", + "TagCommonInclusion", + "TagCompileUnit", + "TagCondition", + "TagConstType", + "TagConstant", + "TagDwarfProcedure", + "TagDynamicType", + "TagEntryPoint", + "TagEnumerationType", + "TagEnumerator", + "TagFileType", + "TagFormalParameter", + "TagFriend", + "TagGenericSubrange", + "TagImmutableType", + "TagImportedDeclaration", + "TagImportedModule", + "TagImportedUnit", + "TagInheritance", + "TagInlinedSubroutine", + "TagInterfaceType", + "TagLabel", + "TagLexDwarfBlock", + "TagMember", + "TagModule", + "TagMutableType", + "TagNamelist", + "TagNamelistItem", + "TagNamespace", + "TagPackedType", + "TagPartialUnit", + "TagPointerType", + "TagPtrToMemberType", + "TagReferenceType", + "TagRestrictType", + "TagRvalueReferenceType", + "TagSetType", + "TagSharedType", + "TagSkeletonUnit", + "TagStringType", + "TagStructType", + "TagSubprogram", + "TagSubrangeType", + "TagSubroutineType", + "TagTemplateAlias", + "TagTemplateTypeParameter", + "TagTemplateValueParameter", + "TagThrownType", + "TagTryDwarfBlock", + "TagTypeUnit", + "TagTypedef", + "TagUnionType", + "TagUnspecifiedParameters", + "TagUnspecifiedType", + "TagVariable", + "TagVariant", + "TagVariantPart", + "TagVolatileType", + "TagWithStmt", + "Type", + "TypedefType", + "UcharType", + "UintType", + "UnspecifiedType", + "UnsupportedType", + "VoidType", + }, + "debug/elf": { + "ARM_MAGIC_TRAMP_NUMBER", + "COMPRESS_HIOS", + "COMPRESS_HIPROC", + "COMPRESS_LOOS", + "COMPRESS_LOPROC", + "COMPRESS_ZLIB", + "Chdr32", + "Chdr64", + "Class", + "CompressionType", + "DF_BIND_NOW", + "DF_ORIGIN", + "DF_STATIC_TLS", + "DF_SYMBOLIC", + "DF_TEXTREL", + "DT_ADDRRNGHI", + "DT_ADDRRNGLO", + "DT_AUDIT", + "DT_AUXILIARY", + "DT_BIND_NOW", + "DT_CHECKSUM", + "DT_CONFIG", + "DT_DEBUG", + "DT_DEPAUDIT", + "DT_ENCODING", + "DT_FEATURE", + "DT_FILTER", + "DT_FINI", + "DT_FINI_ARRAY", + "DT_FINI_ARRAYSZ", + "DT_FLAGS", + "DT_FLAGS_1", + "DT_GNU_CONFLICT", + "DT_GNU_CONFLICTSZ", + "DT_GNU_HASH", + "DT_GNU_LIBLIST", + "DT_GNU_LIBLISTSZ", + "DT_GNU_PRELINKED", + "DT_HASH", + "DT_HIOS", + "DT_HIPROC", + "DT_INIT", + "DT_INIT_ARRAY", + "DT_INIT_ARRAYSZ", + "DT_JMPREL", + "DT_LOOS", + "DT_LOPROC", + "DT_MIPS_AUX_DYNAMIC", + "DT_MIPS_BASE_ADDRESS", + "DT_MIPS_COMPACT_SIZE", + "DT_MIPS_CONFLICT", + "DT_MIPS_CONFLICTNO", + "DT_MIPS_CXX_FLAGS", + "DT_MIPS_DELTA_CLASS", + "DT_MIPS_DELTA_CLASSSYM", + "DT_MIPS_DELTA_CLASSSYM_NO", + "DT_MIPS_DELTA_CLASS_NO", + "DT_MIPS_DELTA_INSTANCE", + "DT_MIPS_DELTA_INSTANCE_NO", + "DT_MIPS_DELTA_RELOC", + "DT_MIPS_DELTA_RELOC_NO", + "DT_MIPS_DELTA_SYM", + "DT_MIPS_DELTA_SYM_NO", + "DT_MIPS_DYNSTR_ALIGN", + "DT_MIPS_FLAGS", + "DT_MIPS_GOTSYM", + "DT_MIPS_GP_VALUE", + "DT_MIPS_HIDDEN_GOTIDX", + "DT_MIPS_HIPAGENO", + "DT_MIPS_ICHECKSUM", + "DT_MIPS_INTERFACE", + "DT_MIPS_INTERFACE_SIZE", + "DT_MIPS_IVERSION", + "DT_MIPS_LIBLIST", + "DT_MIPS_LIBLISTNO", + "DT_MIPS_LOCALPAGE_GOTIDX", + "DT_MIPS_LOCAL_GOTIDX", + "DT_MIPS_LOCAL_GOTNO", + "DT_MIPS_MSYM", + "DT_MIPS_OPTIONS", + "DT_MIPS_PERF_SUFFIX", + "DT_MIPS_PIXIE_INIT", + "DT_MIPS_PLTGOT", + "DT_MIPS_PROTECTED_GOTIDX", + "DT_MIPS_RLD_MAP", + "DT_MIPS_RLD_MAP_REL", + "DT_MIPS_RLD_TEXT_RESOLVE_ADDR", + "DT_MIPS_RLD_VERSION", + "DT_MIPS_RWPLT", + "DT_MIPS_SYMBOL_LIB", + "DT_MIPS_SYMTABNO", + "DT_MIPS_TIME_STAMP", + "DT_MIPS_UNREFEXTNO", + "DT_MOVEENT", + "DT_MOVESZ", + "DT_MOVETAB", + "DT_NEEDED", + "DT_NULL", + "DT_PLTGOT", + "DT_PLTPAD", + "DT_PLTPADSZ", + "DT_PLTREL", + "DT_PLTRELSZ", + "DT_POSFLAG_1", + "DT_PPC64_GLINK", + "DT_PPC64_OPD", + "DT_PPC64_OPDSZ", + "DT_PPC64_OPT", + "DT_PPC_GOT", + "DT_PPC_OPT", + "DT_PREINIT_ARRAY", + "DT_PREINIT_ARRAYSZ", + "DT_REL", + "DT_RELA", + "DT_RELACOUNT", + "DT_RELAENT", + "DT_RELASZ", + "DT_RELCOUNT", + "DT_RELENT", + "DT_RELSZ", + "DT_RPATH", + "DT_RUNPATH", + "DT_SONAME", + "DT_SPARC_REGISTER", + "DT_STRSZ", + "DT_STRTAB", + "DT_SYMBOLIC", + "DT_SYMENT", + "DT_SYMINENT", + "DT_SYMINFO", + "DT_SYMINSZ", + "DT_SYMTAB", + "DT_SYMTAB_SHNDX", + "DT_TEXTREL", + "DT_TLSDESC_GOT", + "DT_TLSDESC_PLT", + "DT_USED", + "DT_VALRNGHI", + "DT_VALRNGLO", + "DT_VERDEF", + "DT_VERDEFNUM", + "DT_VERNEED", + "DT_VERNEEDNUM", + "DT_VERSYM", + "Data", + "Dyn32", + "Dyn64", + "DynFlag", + "DynTag", + "EI_ABIVERSION", + "EI_CLASS", + "EI_DATA", + "EI_NIDENT", + "EI_OSABI", + "EI_PAD", + "EI_VERSION", + "ELFCLASS32", + "ELFCLASS64", + "ELFCLASSNONE", + "ELFDATA2LSB", + "ELFDATA2MSB", + "ELFDATANONE", + "ELFMAG", + "ELFOSABI_86OPEN", + "ELFOSABI_AIX", + "ELFOSABI_ARM", + "ELFOSABI_AROS", + "ELFOSABI_CLOUDABI", + "ELFOSABI_FENIXOS", + "ELFOSABI_FREEBSD", + "ELFOSABI_HPUX", + "ELFOSABI_HURD", + "ELFOSABI_IRIX", + "ELFOSABI_LINUX", + "ELFOSABI_MODESTO", + "ELFOSABI_NETBSD", + "ELFOSABI_NONE", + "ELFOSABI_NSK", + "ELFOSABI_OPENBSD", + "ELFOSABI_OPENVMS", + "ELFOSABI_SOLARIS", + "ELFOSABI_STANDALONE", + "ELFOSABI_TRU64", + "EM_386", + "EM_486", + "EM_56800EX", + "EM_68HC05", + "EM_68HC08", + "EM_68HC11", + "EM_68HC12", + "EM_68HC16", + "EM_68K", + "EM_78KOR", + "EM_8051", + "EM_860", + "EM_88K", + "EM_960", + "EM_AARCH64", + "EM_ALPHA", + "EM_ALPHA_STD", + "EM_ALTERA_NIOS2", + "EM_AMDGPU", + "EM_ARC", + "EM_ARCA", + "EM_ARC_COMPACT", + "EM_ARC_COMPACT2", + "EM_ARM", + "EM_AVR", + "EM_AVR32", + "EM_BA1", + "EM_BA2", + "EM_BLACKFIN", + "EM_BPF", + "EM_C166", + "EM_CDP", + "EM_CE", + "EM_CLOUDSHIELD", + "EM_COGE", + "EM_COLDFIRE", + "EM_COOL", + "EM_COREA_1ST", + "EM_COREA_2ND", + "EM_CR", + "EM_CR16", + "EM_CRAYNV2", + "EM_CRIS", + "EM_CRX", + "EM_CSR_KALIMBA", + "EM_CUDA", + "EM_CYPRESS_M8C", + "EM_D10V", + "EM_D30V", + "EM_DSP24", + "EM_DSPIC30F", + "EM_DXP", + "EM_ECOG1", + "EM_ECOG16", + "EM_ECOG1X", + "EM_ECOG2", + "EM_ETPU", + "EM_EXCESS", + "EM_F2MC16", + "EM_FIREPATH", + "EM_FR20", + "EM_FR30", + "EM_FT32", + "EM_FX66", + "EM_H8S", + "EM_H8_300", + "EM_H8_300H", + "EM_H8_500", + "EM_HUANY", + "EM_IA_64", + "EM_INTEL205", + "EM_INTEL206", + "EM_INTEL207", + "EM_INTEL208", + "EM_INTEL209", + "EM_IP2K", + "EM_JAVELIN", + "EM_K10M", + "EM_KM32", + "EM_KMX16", + "EM_KMX32", + "EM_KMX8", + "EM_KVARC", + "EM_L10M", + "EM_LANAI", + "EM_LATTICEMICO32", + "EM_LOONGARCH", + "EM_M16C", + "EM_M32", + "EM_M32C", + "EM_M32R", + "EM_MANIK", + "EM_MAX", + "EM_MAXQ30", + "EM_MCHP_PIC", + "EM_MCST_ELBRUS", + "EM_ME16", + "EM_METAG", + "EM_MICROBLAZE", + "EM_MIPS", + "EM_MIPS_RS3_LE", + "EM_MIPS_RS4_BE", + "EM_MIPS_X", + "EM_MMA", + "EM_MMDSP_PLUS", + "EM_MMIX", + "EM_MN10200", + "EM_MN10300", + "EM_MOXIE", + "EM_MSP430", + "EM_NCPU", + "EM_NDR1", + "EM_NDS32", + "EM_NONE", + "EM_NORC", + "EM_NS32K", + "EM_OPEN8", + "EM_OPENRISC", + "EM_PARISC", + "EM_PCP", + "EM_PDP10", + "EM_PDP11", + "EM_PDSP", + "EM_PJ", + "EM_PPC", + "EM_PPC64", + "EM_PRISM", + "EM_QDSP6", + "EM_R32C", + "EM_RCE", + "EM_RH32", + "EM_RISCV", + "EM_RL78", + "EM_RS08", + "EM_RX", + "EM_S370", + "EM_S390", + "EM_SCORE7", + "EM_SEP", + "EM_SE_C17", + "EM_SE_C33", + "EM_SH", + "EM_SHARC", + "EM_SLE9X", + "EM_SNP1K", + "EM_SPARC", + "EM_SPARC32PLUS", + "EM_SPARCV9", + "EM_ST100", + "EM_ST19", + "EM_ST200", + "EM_ST7", + "EM_ST9PLUS", + "EM_STARCORE", + "EM_STM8", + "EM_STXP7X", + "EM_SVX", + "EM_TILE64", + "EM_TILEGX", + "EM_TILEPRO", + "EM_TINYJ", + "EM_TI_ARP32", + "EM_TI_C2000", + "EM_TI_C5500", + "EM_TI_C6000", + "EM_TI_PRU", + "EM_TMM_GPP", + "EM_TPC", + "EM_TRICORE", + "EM_TRIMEDIA", + "EM_TSK3000", + "EM_UNICORE", + "EM_V800", + "EM_V850", + "EM_VAX", + "EM_VIDEOCORE", + "EM_VIDEOCORE3", + "EM_VIDEOCORE5", + "EM_VISIUM", + "EM_VPP500", + "EM_X86_64", + "EM_XCORE", + "EM_XGATE", + "EM_XIMO16", + "EM_XTENSA", + "EM_Z80", + "EM_ZSP", + "ET_CORE", + "ET_DYN", + "ET_EXEC", + "ET_HIOS", + "ET_HIPROC", + "ET_LOOS", + "ET_LOPROC", + "ET_NONE", + "ET_REL", + "EV_CURRENT", + "EV_NONE", + "ErrNoSymbols", + "File", + "FileHeader", + "FormatError", + "Header32", + "Header64", + "ImportedSymbol", + "Machine", + "NT_FPREGSET", + "NT_PRPSINFO", + "NT_PRSTATUS", + "NType", + "NewFile", + "OSABI", + "Open", + "PF_MASKOS", + "PF_MASKPROC", + "PF_R", + "PF_W", + "PF_X", + "PT_AARCH64_ARCHEXT", + "PT_AARCH64_UNWIND", + "PT_ARM_ARCHEXT", + "PT_ARM_EXIDX", + "PT_DYNAMIC", + "PT_GNU_EH_FRAME", + "PT_GNU_MBIND_HI", + "PT_GNU_MBIND_LO", + "PT_GNU_PROPERTY", + "PT_GNU_RELRO", + "PT_GNU_STACK", + "PT_HIOS", + "PT_HIPROC", + "PT_INTERP", + "PT_LOAD", + "PT_LOOS", + "PT_LOPROC", + "PT_MIPS_ABIFLAGS", + "PT_MIPS_OPTIONS", + "PT_MIPS_REGINFO", + "PT_MIPS_RTPROC", + "PT_NOTE", + "PT_NULL", + "PT_OPENBSD_BOOTDATA", + "PT_OPENBSD_RANDOMIZE", + "PT_OPENBSD_WXNEEDED", + "PT_PAX_FLAGS", + "PT_PHDR", + "PT_S390_PGSTE", + "PT_SHLIB", + "PT_SUNWSTACK", + "PT_SUNW_EH_FRAME", + "PT_TLS", + "Prog", + "Prog32", + "Prog64", + "ProgFlag", + "ProgHeader", + "ProgType", + "R_386", + "R_386_16", + "R_386_32", + "R_386_32PLT", + "R_386_8", + "R_386_COPY", + "R_386_GLOB_DAT", + "R_386_GOT32", + "R_386_GOT32X", + "R_386_GOTOFF", + "R_386_GOTPC", + "R_386_IRELATIVE", + "R_386_JMP_SLOT", + "R_386_NONE", + "R_386_PC16", + "R_386_PC32", + "R_386_PC8", + "R_386_PLT32", + "R_386_RELATIVE", + "R_386_SIZE32", + "R_386_TLS_DESC", + "R_386_TLS_DESC_CALL", + "R_386_TLS_DTPMOD32", + "R_386_TLS_DTPOFF32", + "R_386_TLS_GD", + "R_386_TLS_GD_32", + "R_386_TLS_GD_CALL", + "R_386_TLS_GD_POP", + "R_386_TLS_GD_PUSH", + "R_386_TLS_GOTDESC", + "R_386_TLS_GOTIE", + "R_386_TLS_IE", + "R_386_TLS_IE_32", + "R_386_TLS_LDM", + "R_386_TLS_LDM_32", + "R_386_TLS_LDM_CALL", + "R_386_TLS_LDM_POP", + "R_386_TLS_LDM_PUSH", + "R_386_TLS_LDO_32", + "R_386_TLS_LE", + "R_386_TLS_LE_32", + "R_386_TLS_TPOFF", + "R_386_TLS_TPOFF32", + "R_390", + "R_390_12", + "R_390_16", + "R_390_20", + "R_390_32", + "R_390_64", + "R_390_8", + "R_390_COPY", + "R_390_GLOB_DAT", + "R_390_GOT12", + "R_390_GOT16", + "R_390_GOT20", + "R_390_GOT32", + "R_390_GOT64", + "R_390_GOTENT", + "R_390_GOTOFF", + "R_390_GOTOFF16", + "R_390_GOTOFF64", + "R_390_GOTPC", + "R_390_GOTPCDBL", + "R_390_GOTPLT12", + "R_390_GOTPLT16", + "R_390_GOTPLT20", + "R_390_GOTPLT32", + "R_390_GOTPLT64", + "R_390_GOTPLTENT", + "R_390_GOTPLTOFF16", + "R_390_GOTPLTOFF32", + "R_390_GOTPLTOFF64", + "R_390_JMP_SLOT", + "R_390_NONE", + "R_390_PC16", + "R_390_PC16DBL", + "R_390_PC32", + "R_390_PC32DBL", + "R_390_PC64", + "R_390_PLT16DBL", + "R_390_PLT32", + "R_390_PLT32DBL", + "R_390_PLT64", + "R_390_RELATIVE", + "R_390_TLS_DTPMOD", + "R_390_TLS_DTPOFF", + "R_390_TLS_GD32", + "R_390_TLS_GD64", + "R_390_TLS_GDCALL", + "R_390_TLS_GOTIE12", + "R_390_TLS_GOTIE20", + "R_390_TLS_GOTIE32", + "R_390_TLS_GOTIE64", + "R_390_TLS_IE32", + "R_390_TLS_IE64", + "R_390_TLS_IEENT", + "R_390_TLS_LDCALL", + "R_390_TLS_LDM32", + "R_390_TLS_LDM64", + "R_390_TLS_LDO32", + "R_390_TLS_LDO64", + "R_390_TLS_LE32", + "R_390_TLS_LE64", + "R_390_TLS_LOAD", + "R_390_TLS_TPOFF", + "R_AARCH64", + "R_AARCH64_ABS16", + "R_AARCH64_ABS32", + "R_AARCH64_ABS64", + "R_AARCH64_ADD_ABS_LO12_NC", + "R_AARCH64_ADR_GOT_PAGE", + "R_AARCH64_ADR_PREL_LO21", + "R_AARCH64_ADR_PREL_PG_HI21", + "R_AARCH64_ADR_PREL_PG_HI21_NC", + "R_AARCH64_CALL26", + "R_AARCH64_CONDBR19", + "R_AARCH64_COPY", + "R_AARCH64_GLOB_DAT", + "R_AARCH64_GOT_LD_PREL19", + "R_AARCH64_IRELATIVE", + "R_AARCH64_JUMP26", + "R_AARCH64_JUMP_SLOT", + "R_AARCH64_LD64_GOTOFF_LO15", + "R_AARCH64_LD64_GOTPAGE_LO15", + "R_AARCH64_LD64_GOT_LO12_NC", + "R_AARCH64_LDST128_ABS_LO12_NC", + "R_AARCH64_LDST16_ABS_LO12_NC", + "R_AARCH64_LDST32_ABS_LO12_NC", + "R_AARCH64_LDST64_ABS_LO12_NC", + "R_AARCH64_LDST8_ABS_LO12_NC", + "R_AARCH64_LD_PREL_LO19", + "R_AARCH64_MOVW_SABS_G0", + "R_AARCH64_MOVW_SABS_G1", + "R_AARCH64_MOVW_SABS_G2", + "R_AARCH64_MOVW_UABS_G0", + "R_AARCH64_MOVW_UABS_G0_NC", + "R_AARCH64_MOVW_UABS_G1", + "R_AARCH64_MOVW_UABS_G1_NC", + "R_AARCH64_MOVW_UABS_G2", + "R_AARCH64_MOVW_UABS_G2_NC", + "R_AARCH64_MOVW_UABS_G3", + "R_AARCH64_NONE", + "R_AARCH64_NULL", + "R_AARCH64_P32_ABS16", + "R_AARCH64_P32_ABS32", + "R_AARCH64_P32_ADD_ABS_LO12_NC", + "R_AARCH64_P32_ADR_GOT_PAGE", + "R_AARCH64_P32_ADR_PREL_LO21", + "R_AARCH64_P32_ADR_PREL_PG_HI21", + "R_AARCH64_P32_CALL26", + "R_AARCH64_P32_CONDBR19", + "R_AARCH64_P32_COPY", + "R_AARCH64_P32_GLOB_DAT", + "R_AARCH64_P32_GOT_LD_PREL19", + "R_AARCH64_P32_IRELATIVE", + "R_AARCH64_P32_JUMP26", + "R_AARCH64_P32_JUMP_SLOT", + "R_AARCH64_P32_LD32_GOT_LO12_NC", + "R_AARCH64_P32_LDST128_ABS_LO12_NC", + "R_AARCH64_P32_LDST16_ABS_LO12_NC", + "R_AARCH64_P32_LDST32_ABS_LO12_NC", + "R_AARCH64_P32_LDST64_ABS_LO12_NC", + "R_AARCH64_P32_LDST8_ABS_LO12_NC", + "R_AARCH64_P32_LD_PREL_LO19", + "R_AARCH64_P32_MOVW_SABS_G0", + "R_AARCH64_P32_MOVW_UABS_G0", + "R_AARCH64_P32_MOVW_UABS_G0_NC", + "R_AARCH64_P32_MOVW_UABS_G1", + "R_AARCH64_P32_PREL16", + "R_AARCH64_P32_PREL32", + "R_AARCH64_P32_RELATIVE", + "R_AARCH64_P32_TLSDESC", + "R_AARCH64_P32_TLSDESC_ADD_LO12_NC", + "R_AARCH64_P32_TLSDESC_ADR_PAGE21", + "R_AARCH64_P32_TLSDESC_ADR_PREL21", + "R_AARCH64_P32_TLSDESC_CALL", + "R_AARCH64_P32_TLSDESC_LD32_LO12_NC", + "R_AARCH64_P32_TLSDESC_LD_PREL19", + "R_AARCH64_P32_TLSGD_ADD_LO12_NC", + "R_AARCH64_P32_TLSGD_ADR_PAGE21", + "R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21", + "R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC", + "R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19", + "R_AARCH64_P32_TLSLE_ADD_TPREL_HI12", + "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12", + "R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC", + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0", + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC", + "R_AARCH64_P32_TLSLE_MOVW_TPREL_G1", + "R_AARCH64_P32_TLS_DTPMOD", + "R_AARCH64_P32_TLS_DTPREL", + "R_AARCH64_P32_TLS_TPREL", + "R_AARCH64_P32_TSTBR14", + "R_AARCH64_PREL16", + "R_AARCH64_PREL32", + "R_AARCH64_PREL64", + "R_AARCH64_RELATIVE", + "R_AARCH64_TLSDESC", + "R_AARCH64_TLSDESC_ADD", + "R_AARCH64_TLSDESC_ADD_LO12_NC", + "R_AARCH64_TLSDESC_ADR_PAGE21", + "R_AARCH64_TLSDESC_ADR_PREL21", + "R_AARCH64_TLSDESC_CALL", + "R_AARCH64_TLSDESC_LD64_LO12_NC", + "R_AARCH64_TLSDESC_LDR", + "R_AARCH64_TLSDESC_LD_PREL19", + "R_AARCH64_TLSDESC_OFF_G0_NC", + "R_AARCH64_TLSDESC_OFF_G1", + "R_AARCH64_TLSGD_ADD_LO12_NC", + "R_AARCH64_TLSGD_ADR_PAGE21", + "R_AARCH64_TLSGD_ADR_PREL21", + "R_AARCH64_TLSGD_MOVW_G0_NC", + "R_AARCH64_TLSGD_MOVW_G1", + "R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21", + "R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC", + "R_AARCH64_TLSIE_LD_GOTTPREL_PREL19", + "R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC", + "R_AARCH64_TLSIE_MOVW_GOTTPREL_G1", + "R_AARCH64_TLSLD_ADR_PAGE21", + "R_AARCH64_TLSLD_ADR_PREL21", + "R_AARCH64_TLSLD_LDST128_DTPREL_LO12", + "R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC", + "R_AARCH64_TLSLE_ADD_TPREL_HI12", + "R_AARCH64_TLSLE_ADD_TPREL_LO12", + "R_AARCH64_TLSLE_ADD_TPREL_LO12_NC", + "R_AARCH64_TLSLE_LDST128_TPREL_LO12", + "R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC", + "R_AARCH64_TLSLE_MOVW_TPREL_G0", + "R_AARCH64_TLSLE_MOVW_TPREL_G0_NC", + "R_AARCH64_TLSLE_MOVW_TPREL_G1", + "R_AARCH64_TLSLE_MOVW_TPREL_G1_NC", + "R_AARCH64_TLSLE_MOVW_TPREL_G2", + "R_AARCH64_TLS_DTPMOD64", + "R_AARCH64_TLS_DTPREL64", + "R_AARCH64_TLS_TPREL64", + "R_AARCH64_TSTBR14", + "R_ALPHA", + "R_ALPHA_BRADDR", + "R_ALPHA_COPY", + "R_ALPHA_GLOB_DAT", + "R_ALPHA_GPDISP", + "R_ALPHA_GPREL32", + "R_ALPHA_GPRELHIGH", + "R_ALPHA_GPRELLOW", + "R_ALPHA_GPVALUE", + "R_ALPHA_HINT", + "R_ALPHA_IMMED_BR_HI32", + "R_ALPHA_IMMED_GP_16", + "R_ALPHA_IMMED_GP_HI32", + "R_ALPHA_IMMED_LO32", + "R_ALPHA_IMMED_SCN_HI32", + "R_ALPHA_JMP_SLOT", + "R_ALPHA_LITERAL", + "R_ALPHA_LITUSE", + "R_ALPHA_NONE", + "R_ALPHA_OP_PRSHIFT", + "R_ALPHA_OP_PSUB", + "R_ALPHA_OP_PUSH", + "R_ALPHA_OP_STORE", + "R_ALPHA_REFLONG", + "R_ALPHA_REFQUAD", + "R_ALPHA_RELATIVE", + "R_ALPHA_SREL16", + "R_ALPHA_SREL32", + "R_ALPHA_SREL64", + "R_ARM", + "R_ARM_ABS12", + "R_ARM_ABS16", + "R_ARM_ABS32", + "R_ARM_ABS32_NOI", + "R_ARM_ABS8", + "R_ARM_ALU_PCREL_15_8", + "R_ARM_ALU_PCREL_23_15", + "R_ARM_ALU_PCREL_7_0", + "R_ARM_ALU_PC_G0", + "R_ARM_ALU_PC_G0_NC", + "R_ARM_ALU_PC_G1", + "R_ARM_ALU_PC_G1_NC", + "R_ARM_ALU_PC_G2", + "R_ARM_ALU_SBREL_19_12_NC", + "R_ARM_ALU_SBREL_27_20_CK", + "R_ARM_ALU_SB_G0", + "R_ARM_ALU_SB_G0_NC", + "R_ARM_ALU_SB_G1", + "R_ARM_ALU_SB_G1_NC", + "R_ARM_ALU_SB_G2", + "R_ARM_AMP_VCALL9", + "R_ARM_BASE_ABS", + "R_ARM_CALL", + "R_ARM_COPY", + "R_ARM_GLOB_DAT", + "R_ARM_GNU_VTENTRY", + "R_ARM_GNU_VTINHERIT", + "R_ARM_GOT32", + "R_ARM_GOTOFF", + "R_ARM_GOTOFF12", + "R_ARM_GOTPC", + "R_ARM_GOTRELAX", + "R_ARM_GOT_ABS", + "R_ARM_GOT_BREL12", + "R_ARM_GOT_PREL", + "R_ARM_IRELATIVE", + "R_ARM_JUMP24", + "R_ARM_JUMP_SLOT", + "R_ARM_LDC_PC_G0", + "R_ARM_LDC_PC_G1", + "R_ARM_LDC_PC_G2", + "R_ARM_LDC_SB_G0", + "R_ARM_LDC_SB_G1", + "R_ARM_LDC_SB_G2", + "R_ARM_LDRS_PC_G0", + "R_ARM_LDRS_PC_G1", + "R_ARM_LDRS_PC_G2", + "R_ARM_LDRS_SB_G0", + "R_ARM_LDRS_SB_G1", + "R_ARM_LDRS_SB_G2", + "R_ARM_LDR_PC_G1", + "R_ARM_LDR_PC_G2", + "R_ARM_LDR_SBREL_11_10_NC", + "R_ARM_LDR_SB_G0", + "R_ARM_LDR_SB_G1", + "R_ARM_LDR_SB_G2", + "R_ARM_ME_TOO", + "R_ARM_MOVT_ABS", + "R_ARM_MOVT_BREL", + "R_ARM_MOVT_PREL", + "R_ARM_MOVW_ABS_NC", + "R_ARM_MOVW_BREL", + "R_ARM_MOVW_BREL_NC", + "R_ARM_MOVW_PREL_NC", + "R_ARM_NONE", + "R_ARM_PC13", + "R_ARM_PC24", + "R_ARM_PLT32", + "R_ARM_PLT32_ABS", + "R_ARM_PREL31", + "R_ARM_PRIVATE_0", + "R_ARM_PRIVATE_1", + "R_ARM_PRIVATE_10", + "R_ARM_PRIVATE_11", + "R_ARM_PRIVATE_12", + "R_ARM_PRIVATE_13", + "R_ARM_PRIVATE_14", + "R_ARM_PRIVATE_15", + "R_ARM_PRIVATE_2", + "R_ARM_PRIVATE_3", + "R_ARM_PRIVATE_4", + "R_ARM_PRIVATE_5", + "R_ARM_PRIVATE_6", + "R_ARM_PRIVATE_7", + "R_ARM_PRIVATE_8", + "R_ARM_PRIVATE_9", + "R_ARM_RABS32", + "R_ARM_RBASE", + "R_ARM_REL32", + "R_ARM_REL32_NOI", + "R_ARM_RELATIVE", + "R_ARM_RPC24", + "R_ARM_RREL32", + "R_ARM_RSBREL32", + "R_ARM_RXPC25", + "R_ARM_SBREL31", + "R_ARM_SBREL32", + "R_ARM_SWI24", + "R_ARM_TARGET1", + "R_ARM_TARGET2", + "R_ARM_THM_ABS5", + "R_ARM_THM_ALU_ABS_G0_NC", + "R_ARM_THM_ALU_ABS_G1_NC", + "R_ARM_THM_ALU_ABS_G2_NC", + "R_ARM_THM_ALU_ABS_G3", + "R_ARM_THM_ALU_PREL_11_0", + "R_ARM_THM_GOT_BREL12", + "R_ARM_THM_JUMP11", + "R_ARM_THM_JUMP19", + "R_ARM_THM_JUMP24", + "R_ARM_THM_JUMP6", + "R_ARM_THM_JUMP8", + "R_ARM_THM_MOVT_ABS", + "R_ARM_THM_MOVT_BREL", + "R_ARM_THM_MOVT_PREL", + "R_ARM_THM_MOVW_ABS_NC", + "R_ARM_THM_MOVW_BREL", + "R_ARM_THM_MOVW_BREL_NC", + "R_ARM_THM_MOVW_PREL_NC", + "R_ARM_THM_PC12", + "R_ARM_THM_PC22", + "R_ARM_THM_PC8", + "R_ARM_THM_RPC22", + "R_ARM_THM_SWI8", + "R_ARM_THM_TLS_CALL", + "R_ARM_THM_TLS_DESCSEQ16", + "R_ARM_THM_TLS_DESCSEQ32", + "R_ARM_THM_XPC22", + "R_ARM_TLS_CALL", + "R_ARM_TLS_DESCSEQ", + "R_ARM_TLS_DTPMOD32", + "R_ARM_TLS_DTPOFF32", + "R_ARM_TLS_GD32", + "R_ARM_TLS_GOTDESC", + "R_ARM_TLS_IE12GP", + "R_ARM_TLS_IE32", + "R_ARM_TLS_LDM32", + "R_ARM_TLS_LDO12", + "R_ARM_TLS_LDO32", + "R_ARM_TLS_LE12", + "R_ARM_TLS_LE32", + "R_ARM_TLS_TPOFF32", + "R_ARM_V4BX", + "R_ARM_XPC25", + "R_INFO", + "R_INFO32", + "R_LARCH", + "R_LARCH_32", + "R_LARCH_64", + "R_LARCH_ADD16", + "R_LARCH_ADD24", + "R_LARCH_ADD32", + "R_LARCH_ADD64", + "R_LARCH_ADD8", + "R_LARCH_COPY", + "R_LARCH_IRELATIVE", + "R_LARCH_JUMP_SLOT", + "R_LARCH_MARK_LA", + "R_LARCH_MARK_PCREL", + "R_LARCH_NONE", + "R_LARCH_RELATIVE", + "R_LARCH_SOP_ADD", + "R_LARCH_SOP_AND", + "R_LARCH_SOP_ASSERT", + "R_LARCH_SOP_IF_ELSE", + "R_LARCH_SOP_NOT", + "R_LARCH_SOP_POP_32_S_0_10_10_16_S2", + "R_LARCH_SOP_POP_32_S_0_5_10_16_S2", + "R_LARCH_SOP_POP_32_S_10_12", + "R_LARCH_SOP_POP_32_S_10_16", + "R_LARCH_SOP_POP_32_S_10_16_S2", + "R_LARCH_SOP_POP_32_S_10_5", + "R_LARCH_SOP_POP_32_S_5_20", + "R_LARCH_SOP_POP_32_U", + "R_LARCH_SOP_POP_32_U_10_12", + "R_LARCH_SOP_PUSH_ABSOLUTE", + "R_LARCH_SOP_PUSH_DUP", + "R_LARCH_SOP_PUSH_GPREL", + "R_LARCH_SOP_PUSH_PCREL", + "R_LARCH_SOP_PUSH_PLT_PCREL", + "R_LARCH_SOP_PUSH_TLS_GD", + "R_LARCH_SOP_PUSH_TLS_GOT", + "R_LARCH_SOP_PUSH_TLS_TPREL", + "R_LARCH_SOP_SL", + "R_LARCH_SOP_SR", + "R_LARCH_SOP_SUB", + "R_LARCH_SUB16", + "R_LARCH_SUB24", + "R_LARCH_SUB32", + "R_LARCH_SUB64", + "R_LARCH_SUB8", + "R_LARCH_TLS_DTPMOD32", + "R_LARCH_TLS_DTPMOD64", + "R_LARCH_TLS_DTPREL32", + "R_LARCH_TLS_DTPREL64", + "R_LARCH_TLS_TPREL32", + "R_LARCH_TLS_TPREL64", + "R_MIPS", + "R_MIPS_16", + "R_MIPS_26", + "R_MIPS_32", + "R_MIPS_64", + "R_MIPS_ADD_IMMEDIATE", + "R_MIPS_CALL16", + "R_MIPS_CALL_HI16", + "R_MIPS_CALL_LO16", + "R_MIPS_DELETE", + "R_MIPS_GOT16", + "R_MIPS_GOT_DISP", + "R_MIPS_GOT_HI16", + "R_MIPS_GOT_LO16", + "R_MIPS_GOT_OFST", + "R_MIPS_GOT_PAGE", + "R_MIPS_GPREL16", + "R_MIPS_GPREL32", + "R_MIPS_HI16", + "R_MIPS_HIGHER", + "R_MIPS_HIGHEST", + "R_MIPS_INSERT_A", + "R_MIPS_INSERT_B", + "R_MIPS_JALR", + "R_MIPS_LITERAL", + "R_MIPS_LO16", + "R_MIPS_NONE", + "R_MIPS_PC16", + "R_MIPS_PJUMP", + "R_MIPS_REL16", + "R_MIPS_REL32", + "R_MIPS_RELGOT", + "R_MIPS_SCN_DISP", + "R_MIPS_SHIFT5", + "R_MIPS_SHIFT6", + "R_MIPS_SUB", + "R_MIPS_TLS_DTPMOD32", + "R_MIPS_TLS_DTPMOD64", + "R_MIPS_TLS_DTPREL32", + "R_MIPS_TLS_DTPREL64", + "R_MIPS_TLS_DTPREL_HI16", + "R_MIPS_TLS_DTPREL_LO16", + "R_MIPS_TLS_GD", + "R_MIPS_TLS_GOTTPREL", + "R_MIPS_TLS_LDM", + "R_MIPS_TLS_TPREL32", + "R_MIPS_TLS_TPREL64", + "R_MIPS_TLS_TPREL_HI16", + "R_MIPS_TLS_TPREL_LO16", + "R_PPC", + "R_PPC64", + "R_PPC64_ADDR14", + "R_PPC64_ADDR14_BRNTAKEN", + "R_PPC64_ADDR14_BRTAKEN", + "R_PPC64_ADDR16", + "R_PPC64_ADDR16_DS", + "R_PPC64_ADDR16_HA", + "R_PPC64_ADDR16_HI", + "R_PPC64_ADDR16_HIGH", + "R_PPC64_ADDR16_HIGHA", + "R_PPC64_ADDR16_HIGHER", + "R_PPC64_ADDR16_HIGHERA", + "R_PPC64_ADDR16_HIGHEST", + "R_PPC64_ADDR16_HIGHESTA", + "R_PPC64_ADDR16_LO", + "R_PPC64_ADDR16_LO_DS", + "R_PPC64_ADDR24", + "R_PPC64_ADDR32", + "R_PPC64_ADDR64", + "R_PPC64_ADDR64_LOCAL", + "R_PPC64_DTPMOD64", + "R_PPC64_DTPREL16", + "R_PPC64_DTPREL16_DS", + "R_PPC64_DTPREL16_HA", + "R_PPC64_DTPREL16_HI", + "R_PPC64_DTPREL16_HIGH", + "R_PPC64_DTPREL16_HIGHA", + "R_PPC64_DTPREL16_HIGHER", + "R_PPC64_DTPREL16_HIGHERA", + "R_PPC64_DTPREL16_HIGHEST", + "R_PPC64_DTPREL16_HIGHESTA", + "R_PPC64_DTPREL16_LO", + "R_PPC64_DTPREL16_LO_DS", + "R_PPC64_DTPREL64", + "R_PPC64_ENTRY", + "R_PPC64_GOT16", + "R_PPC64_GOT16_DS", + "R_PPC64_GOT16_HA", + "R_PPC64_GOT16_HI", + "R_PPC64_GOT16_LO", + "R_PPC64_GOT16_LO_DS", + "R_PPC64_GOT_DTPREL16_DS", + "R_PPC64_GOT_DTPREL16_HA", + "R_PPC64_GOT_DTPREL16_HI", + "R_PPC64_GOT_DTPREL16_LO_DS", + "R_PPC64_GOT_TLSGD16", + "R_PPC64_GOT_TLSGD16_HA", + "R_PPC64_GOT_TLSGD16_HI", + "R_PPC64_GOT_TLSGD16_LO", + "R_PPC64_GOT_TLSLD16", + "R_PPC64_GOT_TLSLD16_HA", + "R_PPC64_GOT_TLSLD16_HI", + "R_PPC64_GOT_TLSLD16_LO", + "R_PPC64_GOT_TPREL16_DS", + "R_PPC64_GOT_TPREL16_HA", + "R_PPC64_GOT_TPREL16_HI", + "R_PPC64_GOT_TPREL16_LO_DS", + "R_PPC64_IRELATIVE", + "R_PPC64_JMP_IREL", + "R_PPC64_JMP_SLOT", + "R_PPC64_NONE", + "R_PPC64_PLT16_LO_DS", + "R_PPC64_PLTGOT16", + "R_PPC64_PLTGOT16_DS", + "R_PPC64_PLTGOT16_HA", + "R_PPC64_PLTGOT16_HI", + "R_PPC64_PLTGOT16_LO", + "R_PPC64_PLTGOT_LO_DS", + "R_PPC64_REL14", + "R_PPC64_REL14_BRNTAKEN", + "R_PPC64_REL14_BRTAKEN", + "R_PPC64_REL16", + "R_PPC64_REL16DX_HA", + "R_PPC64_REL16_HA", + "R_PPC64_REL16_HI", + "R_PPC64_REL16_LO", + "R_PPC64_REL24", + "R_PPC64_REL24_NOTOC", + "R_PPC64_REL32", + "R_PPC64_REL64", + "R_PPC64_RELATIVE", + "R_PPC64_SECTOFF_DS", + "R_PPC64_SECTOFF_LO_DS", + "R_PPC64_TLS", + "R_PPC64_TLSGD", + "R_PPC64_TLSLD", + "R_PPC64_TOC", + "R_PPC64_TOC16", + "R_PPC64_TOC16_DS", + "R_PPC64_TOC16_HA", + "R_PPC64_TOC16_HI", + "R_PPC64_TOC16_LO", + "R_PPC64_TOC16_LO_DS", + "R_PPC64_TOCSAVE", + "R_PPC64_TPREL16", + "R_PPC64_TPREL16_DS", + "R_PPC64_TPREL16_HA", + "R_PPC64_TPREL16_HI", + "R_PPC64_TPREL16_HIGH", + "R_PPC64_TPREL16_HIGHA", + "R_PPC64_TPREL16_HIGHER", + "R_PPC64_TPREL16_HIGHERA", + "R_PPC64_TPREL16_HIGHEST", + "R_PPC64_TPREL16_HIGHESTA", + "R_PPC64_TPREL16_LO", + "R_PPC64_TPREL16_LO_DS", + "R_PPC64_TPREL64", + "R_PPC_ADDR14", + "R_PPC_ADDR14_BRNTAKEN", + "R_PPC_ADDR14_BRTAKEN", + "R_PPC_ADDR16", + "R_PPC_ADDR16_HA", + "R_PPC_ADDR16_HI", + "R_PPC_ADDR16_LO", + "R_PPC_ADDR24", + "R_PPC_ADDR32", + "R_PPC_COPY", + "R_PPC_DTPMOD32", + "R_PPC_DTPREL16", + "R_PPC_DTPREL16_HA", + "R_PPC_DTPREL16_HI", + "R_PPC_DTPREL16_LO", + "R_PPC_DTPREL32", + "R_PPC_EMB_BIT_FLD", + "R_PPC_EMB_MRKREF", + "R_PPC_EMB_NADDR16", + "R_PPC_EMB_NADDR16_HA", + "R_PPC_EMB_NADDR16_HI", + "R_PPC_EMB_NADDR16_LO", + "R_PPC_EMB_NADDR32", + "R_PPC_EMB_RELSDA", + "R_PPC_EMB_RELSEC16", + "R_PPC_EMB_RELST_HA", + "R_PPC_EMB_RELST_HI", + "R_PPC_EMB_RELST_LO", + "R_PPC_EMB_SDA21", + "R_PPC_EMB_SDA2I16", + "R_PPC_EMB_SDA2REL", + "R_PPC_EMB_SDAI16", + "R_PPC_GLOB_DAT", + "R_PPC_GOT16", + "R_PPC_GOT16_HA", + "R_PPC_GOT16_HI", + "R_PPC_GOT16_LO", + "R_PPC_GOT_TLSGD16", + "R_PPC_GOT_TLSGD16_HA", + "R_PPC_GOT_TLSGD16_HI", + "R_PPC_GOT_TLSGD16_LO", + "R_PPC_GOT_TLSLD16", + "R_PPC_GOT_TLSLD16_HA", + "R_PPC_GOT_TLSLD16_HI", + "R_PPC_GOT_TLSLD16_LO", + "R_PPC_GOT_TPREL16", + "R_PPC_GOT_TPREL16_HA", + "R_PPC_GOT_TPREL16_HI", + "R_PPC_GOT_TPREL16_LO", + "R_PPC_JMP_SLOT", + "R_PPC_LOCAL24PC", + "R_PPC_NONE", + "R_PPC_PLT16_HA", + "R_PPC_PLT16_HI", + "R_PPC_PLT16_LO", + "R_PPC_PLT32", + "R_PPC_PLTREL24", + "R_PPC_PLTREL32", + "R_PPC_REL14", + "R_PPC_REL14_BRNTAKEN", + "R_PPC_REL14_BRTAKEN", + "R_PPC_REL24", + "R_PPC_REL32", + "R_PPC_RELATIVE", + "R_PPC_SDAREL16", + "R_PPC_SECTOFF", + "R_PPC_SECTOFF_HA", + "R_PPC_SECTOFF_HI", + "R_PPC_SECTOFF_LO", + "R_PPC_TLS", + "R_PPC_TPREL16", + "R_PPC_TPREL16_HA", + "R_PPC_TPREL16_HI", + "R_PPC_TPREL16_LO", + "R_PPC_TPREL32", + "R_PPC_UADDR16", + "R_PPC_UADDR32", + "R_RISCV", + "R_RISCV_32", + "R_RISCV_32_PCREL", + "R_RISCV_64", + "R_RISCV_ADD16", + "R_RISCV_ADD32", + "R_RISCV_ADD64", + "R_RISCV_ADD8", + "R_RISCV_ALIGN", + "R_RISCV_BRANCH", + "R_RISCV_CALL", + "R_RISCV_CALL_PLT", + "R_RISCV_COPY", + "R_RISCV_GNU_VTENTRY", + "R_RISCV_GNU_VTINHERIT", + "R_RISCV_GOT_HI20", + "R_RISCV_GPREL_I", + "R_RISCV_GPREL_S", + "R_RISCV_HI20", + "R_RISCV_JAL", + "R_RISCV_JUMP_SLOT", + "R_RISCV_LO12_I", + "R_RISCV_LO12_S", + "R_RISCV_NONE", + "R_RISCV_PCREL_HI20", + "R_RISCV_PCREL_LO12_I", + "R_RISCV_PCREL_LO12_S", + "R_RISCV_RELATIVE", + "R_RISCV_RELAX", + "R_RISCV_RVC_BRANCH", + "R_RISCV_RVC_JUMP", + "R_RISCV_RVC_LUI", + "R_RISCV_SET16", + "R_RISCV_SET32", + "R_RISCV_SET6", + "R_RISCV_SET8", + "R_RISCV_SUB16", + "R_RISCV_SUB32", + "R_RISCV_SUB6", + "R_RISCV_SUB64", + "R_RISCV_SUB8", + "R_RISCV_TLS_DTPMOD32", + "R_RISCV_TLS_DTPMOD64", + "R_RISCV_TLS_DTPREL32", + "R_RISCV_TLS_DTPREL64", + "R_RISCV_TLS_GD_HI20", + "R_RISCV_TLS_GOT_HI20", + "R_RISCV_TLS_TPREL32", + "R_RISCV_TLS_TPREL64", + "R_RISCV_TPREL_ADD", + "R_RISCV_TPREL_HI20", + "R_RISCV_TPREL_I", + "R_RISCV_TPREL_LO12_I", + "R_RISCV_TPREL_LO12_S", + "R_RISCV_TPREL_S", + "R_SPARC", + "R_SPARC_10", + "R_SPARC_11", + "R_SPARC_13", + "R_SPARC_16", + "R_SPARC_22", + "R_SPARC_32", + "R_SPARC_5", + "R_SPARC_6", + "R_SPARC_64", + "R_SPARC_7", + "R_SPARC_8", + "R_SPARC_COPY", + "R_SPARC_DISP16", + "R_SPARC_DISP32", + "R_SPARC_DISP64", + "R_SPARC_DISP8", + "R_SPARC_GLOB_DAT", + "R_SPARC_GLOB_JMP", + "R_SPARC_GOT10", + "R_SPARC_GOT13", + "R_SPARC_GOT22", + "R_SPARC_H44", + "R_SPARC_HH22", + "R_SPARC_HI22", + "R_SPARC_HIPLT22", + "R_SPARC_HIX22", + "R_SPARC_HM10", + "R_SPARC_JMP_SLOT", + "R_SPARC_L44", + "R_SPARC_LM22", + "R_SPARC_LO10", + "R_SPARC_LOPLT10", + "R_SPARC_LOX10", + "R_SPARC_M44", + "R_SPARC_NONE", + "R_SPARC_OLO10", + "R_SPARC_PC10", + "R_SPARC_PC22", + "R_SPARC_PCPLT10", + "R_SPARC_PCPLT22", + "R_SPARC_PCPLT32", + "R_SPARC_PC_HH22", + "R_SPARC_PC_HM10", + "R_SPARC_PC_LM22", + "R_SPARC_PLT32", + "R_SPARC_PLT64", + "R_SPARC_REGISTER", + "R_SPARC_RELATIVE", + "R_SPARC_UA16", + "R_SPARC_UA32", + "R_SPARC_UA64", + "R_SPARC_WDISP16", + "R_SPARC_WDISP19", + "R_SPARC_WDISP22", + "R_SPARC_WDISP30", + "R_SPARC_WPLT30", + "R_SYM32", + "R_SYM64", + "R_TYPE32", + "R_TYPE64", + "R_X86_64", + "R_X86_64_16", + "R_X86_64_32", + "R_X86_64_32S", + "R_X86_64_64", + "R_X86_64_8", + "R_X86_64_COPY", + "R_X86_64_DTPMOD64", + "R_X86_64_DTPOFF32", + "R_X86_64_DTPOFF64", + "R_X86_64_GLOB_DAT", + "R_X86_64_GOT32", + "R_X86_64_GOT64", + "R_X86_64_GOTOFF64", + "R_X86_64_GOTPC32", + "R_X86_64_GOTPC32_TLSDESC", + "R_X86_64_GOTPC64", + "R_X86_64_GOTPCREL", + "R_X86_64_GOTPCREL64", + "R_X86_64_GOTPCRELX", + "R_X86_64_GOTPLT64", + "R_X86_64_GOTTPOFF", + "R_X86_64_IRELATIVE", + "R_X86_64_JMP_SLOT", + "R_X86_64_NONE", + "R_X86_64_PC16", + "R_X86_64_PC32", + "R_X86_64_PC32_BND", + "R_X86_64_PC64", + "R_X86_64_PC8", + "R_X86_64_PLT32", + "R_X86_64_PLT32_BND", + "R_X86_64_PLTOFF64", + "R_X86_64_RELATIVE", + "R_X86_64_RELATIVE64", + "R_X86_64_REX_GOTPCRELX", + "R_X86_64_SIZE32", + "R_X86_64_SIZE64", + "R_X86_64_TLSDESC", + "R_X86_64_TLSDESC_CALL", + "R_X86_64_TLSGD", + "R_X86_64_TLSLD", + "R_X86_64_TPOFF32", + "R_X86_64_TPOFF64", + "Rel32", + "Rel64", + "Rela32", + "Rela64", + "SHF_ALLOC", + "SHF_COMPRESSED", + "SHF_EXECINSTR", + "SHF_GROUP", + "SHF_INFO_LINK", + "SHF_LINK_ORDER", + "SHF_MASKOS", + "SHF_MASKPROC", + "SHF_MERGE", + "SHF_OS_NONCONFORMING", + "SHF_STRINGS", + "SHF_TLS", + "SHF_WRITE", + "SHN_ABS", + "SHN_COMMON", + "SHN_HIOS", + "SHN_HIPROC", + "SHN_HIRESERVE", + "SHN_LOOS", + "SHN_LOPROC", + "SHN_LORESERVE", + "SHN_UNDEF", + "SHN_XINDEX", + "SHT_DYNAMIC", + "SHT_DYNSYM", + "SHT_FINI_ARRAY", + "SHT_GNU_ATTRIBUTES", + "SHT_GNU_HASH", + "SHT_GNU_LIBLIST", + "SHT_GNU_VERDEF", + "SHT_GNU_VERNEED", + "SHT_GNU_VERSYM", + "SHT_GROUP", + "SHT_HASH", + "SHT_HIOS", + "SHT_HIPROC", + "SHT_HIUSER", + "SHT_INIT_ARRAY", + "SHT_LOOS", + "SHT_LOPROC", + "SHT_LOUSER", + "SHT_MIPS_ABIFLAGS", + "SHT_NOBITS", + "SHT_NOTE", + "SHT_NULL", + "SHT_PREINIT_ARRAY", + "SHT_PROGBITS", + "SHT_REL", + "SHT_RELA", + "SHT_SHLIB", + "SHT_STRTAB", + "SHT_SYMTAB", + "SHT_SYMTAB_SHNDX", + "STB_GLOBAL", + "STB_HIOS", + "STB_HIPROC", + "STB_LOCAL", + "STB_LOOS", + "STB_LOPROC", + "STB_WEAK", + "STT_COMMON", + "STT_FILE", + "STT_FUNC", + "STT_HIOS", + "STT_HIPROC", + "STT_LOOS", + "STT_LOPROC", + "STT_NOTYPE", + "STT_OBJECT", + "STT_SECTION", + "STT_TLS", + "STV_DEFAULT", + "STV_HIDDEN", + "STV_INTERNAL", + "STV_PROTECTED", + "ST_BIND", + "ST_INFO", + "ST_TYPE", + "ST_VISIBILITY", + "Section", + "Section32", + "Section64", + "SectionFlag", + "SectionHeader", + "SectionIndex", + "SectionType", + "Sym32", + "Sym32Size", + "Sym64", + "Sym64Size", + "SymBind", + "SymType", + "SymVis", + "Symbol", + "Type", + "Version", + }, + "debug/gosym": { + "DecodingError", + "Func", + "LineTable", + "NewLineTable", + "NewTable", + "Obj", + "Sym", + "Table", + "UnknownFileError", + "UnknownLineError", + }, + "debug/macho": { + "ARM64_RELOC_ADDEND", + "ARM64_RELOC_BRANCH26", + "ARM64_RELOC_GOT_LOAD_PAGE21", + "ARM64_RELOC_GOT_LOAD_PAGEOFF12", + "ARM64_RELOC_PAGE21", + "ARM64_RELOC_PAGEOFF12", + "ARM64_RELOC_POINTER_TO_GOT", + "ARM64_RELOC_SUBTRACTOR", + "ARM64_RELOC_TLVP_LOAD_PAGE21", + "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", + "ARM64_RELOC_UNSIGNED", + "ARM_RELOC_BR24", + "ARM_RELOC_HALF", + "ARM_RELOC_HALF_SECTDIFF", + "ARM_RELOC_LOCAL_SECTDIFF", + "ARM_RELOC_PAIR", + "ARM_RELOC_PB_LA_PTR", + "ARM_RELOC_SECTDIFF", + "ARM_RELOC_VANILLA", + "ARM_THUMB_32BIT_BRANCH", + "ARM_THUMB_RELOC_BR22", + "Cpu", + "Cpu386", + "CpuAmd64", + "CpuArm", + "CpuArm64", + "CpuPpc", + "CpuPpc64", + "Dylib", + "DylibCmd", + "Dysymtab", + "DysymtabCmd", + "ErrNotFat", + "FatArch", + "FatArchHeader", + "FatFile", + "File", + "FileHeader", + "FlagAllModsBound", + "FlagAllowStackExecution", + "FlagAppExtensionSafe", + "FlagBindAtLoad", + "FlagBindsToWeak", + "FlagCanonical", + "FlagDeadStrippableDylib", + "FlagDyldLink", + "FlagForceFlat", + "FlagHasTLVDescriptors", + "FlagIncrLink", + "FlagLazyInit", + "FlagNoFixPrebinding", + "FlagNoHeapExecution", + "FlagNoMultiDefs", + "FlagNoReexportedDylibs", + "FlagNoUndefs", + "FlagPIE", + "FlagPrebindable", + "FlagPrebound", + "FlagRootSafe", + "FlagSetuidSafe", + "FlagSplitSegs", + "FlagSubsectionsViaSymbols", + "FlagTwoLevel", + "FlagWeakDefines", + "FormatError", + "GENERIC_RELOC_LOCAL_SECTDIFF", + "GENERIC_RELOC_PAIR", + "GENERIC_RELOC_PB_LA_PTR", + "GENERIC_RELOC_SECTDIFF", + "GENERIC_RELOC_TLV", + "GENERIC_RELOC_VANILLA", + "Load", + "LoadBytes", + "LoadCmd", + "LoadCmdDylib", + "LoadCmdDylinker", + "LoadCmdDysymtab", + "LoadCmdRpath", + "LoadCmdSegment", + "LoadCmdSegment64", + "LoadCmdSymtab", + "LoadCmdThread", + "LoadCmdUnixThread", + "Magic32", + "Magic64", + "MagicFat", + "NewFatFile", + "NewFile", + "Nlist32", + "Nlist64", + "Open", + "OpenFat", + "Regs386", + "RegsAMD64", + "Reloc", + "RelocTypeARM", + "RelocTypeARM64", + "RelocTypeGeneric", + "RelocTypeX86_64", + "Rpath", + "RpathCmd", + "Section", + "Section32", + "Section64", + "SectionHeader", + "Segment", + "Segment32", + "Segment64", + "SegmentHeader", + "Symbol", + "Symtab", + "SymtabCmd", + "Thread", + "Type", + "TypeBundle", + "TypeDylib", + "TypeExec", + "TypeObj", + "X86_64_RELOC_BRANCH", + "X86_64_RELOC_GOT", + "X86_64_RELOC_GOT_LOAD", + "X86_64_RELOC_SIGNED", + "X86_64_RELOC_SIGNED_1", + "X86_64_RELOC_SIGNED_2", + "X86_64_RELOC_SIGNED_4", + "X86_64_RELOC_SUBTRACTOR", + "X86_64_RELOC_TLV", + "X86_64_RELOC_UNSIGNED", + }, + "debug/pe": { + "COFFSymbol", + "COFFSymbolAuxFormat5", + "COFFSymbolSize", + "DataDirectory", + "File", + "FileHeader", + "FormatError", + "IMAGE_COMDAT_SELECT_ANY", + "IMAGE_COMDAT_SELECT_ASSOCIATIVE", + "IMAGE_COMDAT_SELECT_EXACT_MATCH", + "IMAGE_COMDAT_SELECT_LARGEST", + "IMAGE_COMDAT_SELECT_NODUPLICATES", + "IMAGE_COMDAT_SELECT_SAME_SIZE", + "IMAGE_DIRECTORY_ENTRY_ARCHITECTURE", + "IMAGE_DIRECTORY_ENTRY_BASERELOC", + "IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", + "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", + "IMAGE_DIRECTORY_ENTRY_DEBUG", + "IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", + "IMAGE_DIRECTORY_ENTRY_EXCEPTION", + "IMAGE_DIRECTORY_ENTRY_EXPORT", + "IMAGE_DIRECTORY_ENTRY_GLOBALPTR", + "IMAGE_DIRECTORY_ENTRY_IAT", + "IMAGE_DIRECTORY_ENTRY_IMPORT", + "IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", + "IMAGE_DIRECTORY_ENTRY_RESOURCE", + "IMAGE_DIRECTORY_ENTRY_SECURITY", + "IMAGE_DIRECTORY_ENTRY_TLS", + "IMAGE_DLLCHARACTERISTICS_APPCONTAINER", + "IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", + "IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY", + "IMAGE_DLLCHARACTERISTICS_GUARD_CF", + "IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA", + "IMAGE_DLLCHARACTERISTICS_NO_BIND", + "IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", + "IMAGE_DLLCHARACTERISTICS_NO_SEH", + "IMAGE_DLLCHARACTERISTICS_NX_COMPAT", + "IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", + "IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", + "IMAGE_FILE_32BIT_MACHINE", + "IMAGE_FILE_AGGRESIVE_WS_TRIM", + "IMAGE_FILE_BYTES_REVERSED_HI", + "IMAGE_FILE_BYTES_REVERSED_LO", + "IMAGE_FILE_DEBUG_STRIPPED", + "IMAGE_FILE_DLL", + "IMAGE_FILE_EXECUTABLE_IMAGE", + "IMAGE_FILE_LARGE_ADDRESS_AWARE", + "IMAGE_FILE_LINE_NUMS_STRIPPED", + "IMAGE_FILE_LOCAL_SYMS_STRIPPED", + "IMAGE_FILE_MACHINE_AM33", + "IMAGE_FILE_MACHINE_AMD64", + "IMAGE_FILE_MACHINE_ARM", + "IMAGE_FILE_MACHINE_ARM64", + "IMAGE_FILE_MACHINE_ARMNT", + "IMAGE_FILE_MACHINE_EBC", + "IMAGE_FILE_MACHINE_I386", + "IMAGE_FILE_MACHINE_IA64", + "IMAGE_FILE_MACHINE_LOONGARCH32", + "IMAGE_FILE_MACHINE_LOONGARCH64", + "IMAGE_FILE_MACHINE_M32R", + "IMAGE_FILE_MACHINE_MIPS16", + "IMAGE_FILE_MACHINE_MIPSFPU", + "IMAGE_FILE_MACHINE_MIPSFPU16", + "IMAGE_FILE_MACHINE_POWERPC", + "IMAGE_FILE_MACHINE_POWERPCFP", + "IMAGE_FILE_MACHINE_R4000", + "IMAGE_FILE_MACHINE_SH3", + "IMAGE_FILE_MACHINE_SH3DSP", + "IMAGE_FILE_MACHINE_SH4", + "IMAGE_FILE_MACHINE_SH5", + "IMAGE_FILE_MACHINE_THUMB", + "IMAGE_FILE_MACHINE_UNKNOWN", + "IMAGE_FILE_MACHINE_WCEMIPSV2", + "IMAGE_FILE_NET_RUN_FROM_SWAP", + "IMAGE_FILE_RELOCS_STRIPPED", + "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", + "IMAGE_FILE_SYSTEM", + "IMAGE_FILE_UP_SYSTEM_ONLY", + "IMAGE_SCN_CNT_CODE", + "IMAGE_SCN_CNT_INITIALIZED_DATA", + "IMAGE_SCN_CNT_UNINITIALIZED_DATA", + "IMAGE_SCN_LNK_COMDAT", + "IMAGE_SCN_MEM_DISCARDABLE", + "IMAGE_SCN_MEM_EXECUTE", + "IMAGE_SCN_MEM_READ", + "IMAGE_SCN_MEM_WRITE", + "IMAGE_SUBSYSTEM_EFI_APPLICATION", + "IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER", + "IMAGE_SUBSYSTEM_EFI_ROM", + "IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER", + "IMAGE_SUBSYSTEM_NATIVE", + "IMAGE_SUBSYSTEM_NATIVE_WINDOWS", + "IMAGE_SUBSYSTEM_OS2_CUI", + "IMAGE_SUBSYSTEM_POSIX_CUI", + "IMAGE_SUBSYSTEM_UNKNOWN", + "IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION", + "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", + "IMAGE_SUBSYSTEM_WINDOWS_CUI", + "IMAGE_SUBSYSTEM_WINDOWS_GUI", + "IMAGE_SUBSYSTEM_XBOX", + "ImportDirectory", + "NewFile", + "Open", + "OptionalHeader32", + "OptionalHeader64", + "Reloc", + "Section", + "SectionHeader", + "SectionHeader32", + "StringTable", + "Symbol", + }, + "debug/plan9obj": { + "ErrNoSymbols", + "File", + "FileHeader", + "Magic386", + "Magic64", + "MagicAMD64", + "MagicARM", + "NewFile", + "Open", + "Section", + "SectionHeader", + "Sym", + }, + "embed": { + "FS", + }, + "encoding": { + "BinaryMarshaler", + "BinaryUnmarshaler", + "TextMarshaler", + "TextUnmarshaler", + }, + "encoding/ascii85": { + "CorruptInputError", + "Decode", + "Encode", + "MaxEncodedLen", + "NewDecoder", + "NewEncoder", + }, + "encoding/asn1": { + "BitString", + "ClassApplication", + "ClassContextSpecific", + "ClassPrivate", + "ClassUniversal", + "Enumerated", + "Flag", + "Marshal", + "MarshalWithParams", + "NullBytes", + "NullRawValue", + "ObjectIdentifier", + "RawContent", + "RawValue", + "StructuralError", + "SyntaxError", + "TagBMPString", + "TagBitString", + "TagBoolean", + "TagEnum", + "TagGeneralString", + "TagGeneralizedTime", + "TagIA5String", + "TagInteger", + "TagNull", + "TagNumericString", + "TagOID", + "TagOctetString", + "TagPrintableString", + "TagSequence", + "TagSet", + "TagT61String", + "TagUTCTime", + "TagUTF8String", + "Unmarshal", + "UnmarshalWithParams", + }, + "encoding/base32": { + "CorruptInputError", + "Encoding", + "HexEncoding", + "NewDecoder", + "NewEncoder", + "NewEncoding", + "NoPadding", + "StdEncoding", + "StdPadding", + }, + "encoding/base64": { + "CorruptInputError", + "Encoding", + "NewDecoder", + "NewEncoder", + "NewEncoding", + "NoPadding", + "RawStdEncoding", + "RawURLEncoding", + "StdEncoding", + "StdPadding", + "URLEncoding", + }, + "encoding/binary": { + "AppendByteOrder", + "AppendUvarint", + "AppendVarint", + "BigEndian", + "ByteOrder", + "LittleEndian", + "MaxVarintLen16", + "MaxVarintLen32", + "MaxVarintLen64", + "PutUvarint", + "PutVarint", + "Read", + "ReadUvarint", + "ReadVarint", + "Size", + "Uvarint", + "Varint", + "Write", + }, + "encoding/csv": { + "ErrBareQuote", + "ErrFieldCount", + "ErrQuote", + "ErrTrailingComma", + "NewReader", + "NewWriter", + "ParseError", + "Reader", + "Writer", + }, + "encoding/gob": { + "CommonType", + "Decoder", + "Encoder", + "GobDecoder", + "GobEncoder", + "NewDecoder", + "NewEncoder", + "Register", + "RegisterName", + }, + "encoding/hex": { + "Decode", + "DecodeString", + "DecodedLen", + "Dump", + "Dumper", + "Encode", + "EncodeToString", + "EncodedLen", + "ErrLength", + "InvalidByteError", + "NewDecoder", + "NewEncoder", + }, + "encoding/json": { + "Compact", + "Decoder", + "Delim", + "Encoder", + "HTMLEscape", + "Indent", + "InvalidUTF8Error", + "InvalidUnmarshalError", + "Marshal", + "MarshalIndent", + "Marshaler", + "MarshalerError", + "NewDecoder", + "NewEncoder", + "Number", + "RawMessage", + "SyntaxError", + "Token", + "Unmarshal", + "UnmarshalFieldError", + "UnmarshalTypeError", + "Unmarshaler", + "UnsupportedTypeError", + "UnsupportedValueError", + "Valid", + }, + "encoding/pem": { + "Block", + "Decode", + "Encode", + "EncodeToMemory", + }, + "encoding/xml": { + "Attr", + "CharData", + "Comment", + "CopyToken", + "Decoder", + "Directive", + "Encoder", + "EndElement", + "Escape", + "EscapeText", + "HTMLAutoClose", + "HTMLEntity", + "Header", + "Marshal", + "MarshalIndent", + "Marshaler", + "MarshalerAttr", + "Name", + "NewDecoder", + "NewEncoder", + "NewTokenDecoder", + "ProcInst", + "StartElement", + "SyntaxError", + "TagPathError", + "Token", + "TokenReader", + "Unmarshal", + "UnmarshalError", + "Unmarshaler", + "UnmarshalerAttr", + "UnsupportedTypeError", + }, + "errors": { + "As", + "Is", + "New", + "Unwrap", + }, + "expvar": { + "Do", + "Float", + "Func", + "Get", + "Handler", + "Int", + "KeyValue", + "Map", + "NewFloat", + "NewInt", + "NewMap", + "NewString", + "Publish", + "String", + "Var", + }, + "flag": { + "Arg", + "Args", + "Bool", + "BoolVar", + "CommandLine", + "ContinueOnError", + "Duration", + "DurationVar", + "ErrHelp", + "ErrorHandling", + "ExitOnError", + "Flag", + "FlagSet", + "Float64", + "Float64Var", + "Func", + "Getter", + "Int", + "Int64", + "Int64Var", + "IntVar", + "Lookup", + "NArg", + "NFlag", + "NewFlagSet", + "PanicOnError", + "Parse", + "Parsed", + "PrintDefaults", + "Set", + "String", + "StringVar", + "TextVar", + "Uint", + "Uint64", + "Uint64Var", + "UintVar", + "UnquoteUsage", + "Usage", + "Value", + "Var", + "Visit", + "VisitAll", + }, + "fmt": { + "Append", + "Appendf", + "Appendln", + "Errorf", + "Formatter", + "Fprint", + "Fprintf", + "Fprintln", + "Fscan", + "Fscanf", + "Fscanln", + "GoStringer", + "Print", + "Printf", + "Println", + "Scan", + "ScanState", + "Scanf", + "Scanln", + "Scanner", + "Sprint", + "Sprintf", + "Sprintln", + "Sscan", + "Sscanf", + "Sscanln", + "State", + "Stringer", + }, + "go/ast": { + "ArrayType", + "AssignStmt", + "Bad", + "BadDecl", + "BadExpr", + "BadStmt", + "BasicLit", + "BinaryExpr", + "BlockStmt", + "BranchStmt", + "CallExpr", + "CaseClause", + "ChanDir", + "ChanType", + "CommClause", + "Comment", + "CommentGroup", + "CommentMap", + "CompositeLit", + "Con", + "Decl", + "DeclStmt", + "DeferStmt", + "Ellipsis", + "EmptyStmt", + "Expr", + "ExprStmt", + "Field", + "FieldFilter", + "FieldList", + "File", + "FileExports", + "Filter", + "FilterDecl", + "FilterFile", + "FilterFuncDuplicates", + "FilterImportDuplicates", + "FilterPackage", + "FilterUnassociatedComments", + "ForStmt", + "Fprint", + "Fun", + "FuncDecl", + "FuncLit", + "FuncType", + "GenDecl", + "GoStmt", + "Ident", + "IfStmt", + "ImportSpec", + "Importer", + "IncDecStmt", + "IndexExpr", + "IndexListExpr", + "Inspect", + "InterfaceType", + "IsExported", + "KeyValueExpr", + "LabeledStmt", + "Lbl", + "MapType", + "MergeMode", + "MergePackageFiles", + "NewCommentMap", + "NewIdent", + "NewObj", + "NewPackage", + "NewScope", + "Node", + "NotNilFilter", + "ObjKind", + "Object", + "Package", + "PackageExports", + "ParenExpr", + "Pkg", + "Print", + "RECV", + "RangeStmt", + "ReturnStmt", + "SEND", + "Scope", + "SelectStmt", + "SelectorExpr", + "SendStmt", + "SliceExpr", + "SortImports", + "Spec", + "StarExpr", + "Stmt", + "StructType", + "SwitchStmt", + "Typ", + "TypeAssertExpr", + "TypeSpec", + "TypeSwitchStmt", + "UnaryExpr", + "ValueSpec", + "Var", + "Visitor", + "Walk", + }, + "go/build": { + "AllowBinary", + "ArchChar", + "Context", + "Default", + "FindOnly", + "IgnoreVendor", + "Import", + "ImportComment", + "ImportDir", + "ImportMode", + "IsLocalImport", + "MultiplePackageError", + "NoGoError", + "Package", + "ToolDir", + }, + "go/build/constraint": { + "AndExpr", + "Expr", + "IsGoBuild", + "IsPlusBuild", + "NotExpr", + "OrExpr", + "Parse", + "PlusBuildLines", + "SyntaxError", + "TagExpr", + }, + "go/constant": { + "BinaryOp", + "BitLen", + "Bool", + "BoolVal", + "Bytes", + "Compare", + "Complex", + "Denom", + "Float", + "Float32Val", + "Float64Val", + "Imag", + "Int", + "Int64Val", + "Kind", + "Make", + "MakeBool", + "MakeFloat64", + "MakeFromBytes", + "MakeFromLiteral", + "MakeImag", + "MakeInt64", + "MakeString", + "MakeUint64", + "MakeUnknown", + "Num", + "Real", + "Shift", + "Sign", + "String", + "StringVal", + "ToComplex", + "ToFloat", + "ToInt", + "Uint64Val", + "UnaryOp", + "Unknown", + "Val", + "Value", + }, + "go/doc": { + "AllDecls", + "AllMethods", + "Example", + "Examples", + "Filter", + "Func", + "IllegalPrefixes", + "IsPredeclared", + "Mode", + "New", + "NewFromFiles", + "Note", + "Package", + "PreserveAST", + "Synopsis", + "ToHTML", + "ToText", + "Type", + "Value", + }, + "go/doc/comment": { + "Block", + "Code", + "DefaultLookupPackage", + "Doc", + "DocLink", + "Heading", + "Italic", + "Link", + "LinkDef", + "List", + "ListItem", + "Paragraph", + "Parser", + "Plain", + "Printer", + "Text", + }, + "go/format": { + "Node", + "Source", + }, + "go/importer": { + "Default", + "For", + "ForCompiler", + "Lookup", + }, + "go/parser": { + "AllErrors", + "DeclarationErrors", + "ImportsOnly", + "Mode", + "PackageClauseOnly", + "ParseComments", + "ParseDir", + "ParseExpr", + "ParseExprFrom", + "ParseFile", + "SkipObjectResolution", + "SpuriousErrors", + "Trace", + }, + "go/printer": { + "CommentedNode", + "Config", + "Fprint", + "Mode", + "RawFormat", + "SourcePos", + "TabIndent", + "UseSpaces", + }, + "go/scanner": { + "Error", + "ErrorHandler", + "ErrorList", + "Mode", + "PrintError", + "ScanComments", + "Scanner", + }, + "go/token": { + "ADD", + "ADD_ASSIGN", + "AND", + "AND_ASSIGN", + "AND_NOT", + "AND_NOT_ASSIGN", + "ARROW", + "ASSIGN", + "BREAK", + "CASE", + "CHAN", + "CHAR", + "COLON", + "COMMA", + "COMMENT", + "CONST", + "CONTINUE", + "DEC", + "DEFAULT", + "DEFER", + "DEFINE", + "ELLIPSIS", + "ELSE", + "EOF", + "EQL", + "FALLTHROUGH", + "FLOAT", + "FOR", + "FUNC", + "File", + "FileSet", + "GEQ", + "GO", + "GOTO", + "GTR", + "HighestPrec", + "IDENT", + "IF", + "ILLEGAL", + "IMAG", + "IMPORT", + "INC", + "INT", + "INTERFACE", + "IsExported", + "IsIdentifier", + "IsKeyword", + "LAND", + "LBRACE", + "LBRACK", + "LEQ", + "LOR", + "LPAREN", + "LSS", + "Lookup", + "LowestPrec", + "MAP", + "MUL", + "MUL_ASSIGN", + "NEQ", + "NOT", + "NewFileSet", + "NoPos", + "OR", + "OR_ASSIGN", + "PACKAGE", + "PERIOD", + "Pos", + "Position", + "QUO", + "QUO_ASSIGN", + "RANGE", + "RBRACE", + "RBRACK", + "REM", + "REM_ASSIGN", + "RETURN", + "RPAREN", + "SELECT", + "SEMICOLON", + "SHL", + "SHL_ASSIGN", + "SHR", + "SHR_ASSIGN", + "STRING", + "STRUCT", + "SUB", + "SUB_ASSIGN", + "SWITCH", + "TILDE", + "TYPE", + "Token", + "UnaryPrec", + "VAR", + "XOR", + "XOR_ASSIGN", + }, + "go/types": { + "ArgumentError", + "Array", + "AssertableTo", + "AssignableTo", + "Basic", + "BasicInfo", + "BasicKind", + "Bool", + "Builtin", + "Byte", + "Chan", + "ChanDir", + "CheckExpr", + "Checker", + "Comparable", + "Complex128", + "Complex64", + "Config", + "Const", + "Context", + "ConvertibleTo", + "DefPredeclaredTestFuncs", + "Default", + "Error", + "Eval", + "ExprString", + "FieldVal", + "Float32", + "Float64", + "Func", + "Id", + "Identical", + "IdenticalIgnoreTags", + "Implements", + "ImportMode", + "Importer", + "ImporterFrom", + "Info", + "Initializer", + "Instance", + "Instantiate", + "Int", + "Int16", + "Int32", + "Int64", + "Int8", + "Interface", + "Invalid", + "IsBoolean", + "IsComplex", + "IsConstType", + "IsFloat", + "IsInteger", + "IsInterface", + "IsNumeric", + "IsOrdered", + "IsString", + "IsUnsigned", + "IsUntyped", + "Label", + "LookupFieldOrMethod", + "Map", + "MethodExpr", + "MethodSet", + "MethodVal", + "MissingMethod", + "Named", + "NewArray", + "NewChan", + "NewChecker", + "NewConst", + "NewContext", + "NewField", + "NewFunc", + "NewInterface", + "NewInterfaceType", + "NewLabel", + "NewMap", + "NewMethodSet", + "NewNamed", + "NewPackage", + "NewParam", + "NewPkgName", + "NewPointer", + "NewScope", + "NewSignature", + "NewSignatureType", + "NewSlice", + "NewStruct", + "NewTerm", + "NewTuple", + "NewTypeName", + "NewTypeParam", + "NewUnion", + "NewVar", + "Nil", + "Object", + "ObjectString", + "Package", + "PkgName", + "Pointer", + "Qualifier", + "RecvOnly", + "RelativeTo", + "Rune", + "Scope", + "Selection", + "SelectionKind", + "SelectionString", + "SendOnly", + "SendRecv", + "Signature", + "Sizes", + "SizesFor", + "Slice", + "StdSizes", + "String", + "Struct", + "Term", + "Tuple", + "Typ", + "Type", + "TypeAndValue", + "TypeList", + "TypeName", + "TypeParam", + "TypeParamList", + "TypeString", + "Uint", + "Uint16", + "Uint32", + "Uint64", + "Uint8", + "Uintptr", + "Union", + "Universe", + "Unsafe", + "UnsafePointer", + "UntypedBool", + "UntypedComplex", + "UntypedFloat", + "UntypedInt", + "UntypedNil", + "UntypedRune", + "UntypedString", + "Var", + "WriteExpr", + "WriteSignature", + "WriteType", + }, + "hash": { + "Hash", + "Hash32", + "Hash64", + }, + "hash/adler32": { + "Checksum", + "New", + "Size", + }, + "hash/crc32": { + "Castagnoli", + "Checksum", + "ChecksumIEEE", + "IEEE", + "IEEETable", + "Koopman", + "MakeTable", + "New", + "NewIEEE", + "Size", + "Table", + "Update", + }, + "hash/crc64": { + "Checksum", + "ECMA", + "ISO", + "MakeTable", + "New", + "Size", + "Table", + "Update", + }, + "hash/fnv": { + "New128", + "New128a", + "New32", + "New32a", + "New64", + "New64a", + }, + "hash/maphash": { + "Bytes", + "Hash", + "MakeSeed", + "Seed", + "String", + }, + "html": { + "EscapeString", + "UnescapeString", + }, + "html/template": { + "CSS", + "ErrAmbigContext", + "ErrBadHTML", + "ErrBranchEnd", + "ErrEndContext", + "ErrNoSuchTemplate", + "ErrOutputContext", + "ErrPartialCharset", + "ErrPartialEscape", + "ErrPredefinedEscaper", + "ErrRangeLoopReentry", + "ErrSlashAmbig", + "Error", + "ErrorCode", + "FuncMap", + "HTML", + "HTMLAttr", + "HTMLEscape", + "HTMLEscapeString", + "HTMLEscaper", + "IsTrue", + "JS", + "JSEscape", + "JSEscapeString", + "JSEscaper", + "JSStr", + "Must", + "New", + "OK", + "ParseFS", + "ParseFiles", + "ParseGlob", + "Srcset", + "Template", + "URL", + "URLQueryEscaper", + }, + "image": { + "Alpha", + "Alpha16", + "Black", + "CMYK", + "Config", + "Decode", + "DecodeConfig", + "ErrFormat", + "Gray", + "Gray16", + "Image", + "NRGBA", + "NRGBA64", + "NYCbCrA", + "NewAlpha", + "NewAlpha16", + "NewCMYK", + "NewGray", + "NewGray16", + "NewNRGBA", + "NewNRGBA64", + "NewNYCbCrA", + "NewPaletted", + "NewRGBA", + "NewRGBA64", + "NewUniform", + "NewYCbCr", + "Opaque", + "Paletted", + "PalettedImage", + "Point", + "Pt", + "RGBA", + "RGBA64", + "RGBA64Image", + "Rect", + "Rectangle", + "RegisterFormat", + "Transparent", + "Uniform", + "White", + "YCbCr", + "YCbCrSubsampleRatio", + "YCbCrSubsampleRatio410", + "YCbCrSubsampleRatio411", + "YCbCrSubsampleRatio420", + "YCbCrSubsampleRatio422", + "YCbCrSubsampleRatio440", + "YCbCrSubsampleRatio444", + "ZP", + "ZR", + }, + "image/color": { + "Alpha", + "Alpha16", + "Alpha16Model", + "AlphaModel", + "Black", + "CMYK", + "CMYKModel", + "CMYKToRGB", + "Color", + "Gray", + "Gray16", + "Gray16Model", + "GrayModel", + "Model", + "ModelFunc", + "NRGBA", + "NRGBA64", + "NRGBA64Model", + "NRGBAModel", + "NYCbCrA", + "NYCbCrAModel", + "Opaque", + "Palette", + "RGBA", + "RGBA64", + "RGBA64Model", + "RGBAModel", + "RGBToCMYK", + "RGBToYCbCr", + "Transparent", + "White", + "YCbCr", + "YCbCrModel", + "YCbCrToRGB", + }, + "image/color/palette": { + "Plan9", + "WebSafe", + }, + "image/draw": { + "Draw", + "DrawMask", + "Drawer", + "FloydSteinberg", + "Image", + "Op", + "Over", + "Quantizer", + "RGBA64Image", + "Src", + }, + "image/gif": { + "Decode", + "DecodeAll", + "DecodeConfig", + "DisposalBackground", + "DisposalNone", + "DisposalPrevious", + "Encode", + "EncodeAll", + "GIF", + "Options", + }, + "image/jpeg": { + "Decode", + "DecodeConfig", + "DefaultQuality", + "Encode", + "FormatError", + "Options", + "Reader", + "UnsupportedError", + }, + "image/png": { + "BestCompression", + "BestSpeed", + "CompressionLevel", + "Decode", + "DecodeConfig", + "DefaultCompression", + "Encode", + "Encoder", + "EncoderBuffer", + "EncoderBufferPool", + "FormatError", + "NoCompression", + "UnsupportedError", + }, + "index/suffixarray": { + "Index", + "New", + }, + "io": { + "ByteReader", + "ByteScanner", + "ByteWriter", + "Closer", + "Copy", + "CopyBuffer", + "CopyN", + "Discard", + "EOF", + "ErrClosedPipe", + "ErrNoProgress", + "ErrShortBuffer", + "ErrShortWrite", + "ErrUnexpectedEOF", + "LimitReader", + "LimitedReader", + "MultiReader", + "MultiWriter", + "NewSectionReader", + "NopCloser", + "Pipe", + "PipeReader", + "PipeWriter", + "ReadAll", + "ReadAtLeast", + "ReadCloser", + "ReadFull", + "ReadSeekCloser", + "ReadSeeker", + "ReadWriteCloser", + "ReadWriteSeeker", + "ReadWriter", + "Reader", + "ReaderAt", + "ReaderFrom", + "RuneReader", + "RuneScanner", + "SectionReader", + "SeekCurrent", + "SeekEnd", + "SeekStart", + "Seeker", + "StringWriter", + "TeeReader", + "WriteCloser", + "WriteSeeker", + "WriteString", + "Writer", + "WriterAt", + "WriterTo", + }, + "io/fs": { + "DirEntry", + "ErrClosed", + "ErrExist", + "ErrInvalid", + "ErrNotExist", + "ErrPermission", + "FS", + "File", + "FileInfo", + "FileInfoToDirEntry", + "FileMode", + "Glob", + "GlobFS", + "ModeAppend", + "ModeCharDevice", + "ModeDevice", + "ModeDir", + "ModeExclusive", + "ModeIrregular", + "ModeNamedPipe", + "ModePerm", + "ModeSetgid", + "ModeSetuid", + "ModeSocket", + "ModeSticky", + "ModeSymlink", + "ModeTemporary", + "ModeType", + "PathError", + "ReadDir", + "ReadDirFS", + "ReadDirFile", + "ReadFile", + "ReadFileFS", + "SkipDir", + "Stat", + "StatFS", + "Sub", + "SubFS", + "ValidPath", + "WalkDir", + "WalkDirFunc", + }, + "io/ioutil": { + "Discard", + "NopCloser", + "ReadAll", + "ReadDir", + "ReadFile", + "TempDir", + "TempFile", + "WriteFile", + }, + "log": { + "Default", + "Fatal", + "Fatalf", + "Fatalln", + "Flags", + "LUTC", + "Ldate", + "Llongfile", + "Lmicroseconds", + "Lmsgprefix", + "Logger", + "Lshortfile", + "LstdFlags", + "Ltime", + "New", + "Output", + "Panic", + "Panicf", + "Panicln", + "Prefix", + "Print", + "Printf", + "Println", + "SetFlags", + "SetOutput", + "SetPrefix", + "Writer", + }, + "log/syslog": { + "Dial", + "LOG_ALERT", + "LOG_AUTH", + "LOG_AUTHPRIV", + "LOG_CRIT", + "LOG_CRON", + "LOG_DAEMON", + "LOG_DEBUG", + "LOG_EMERG", + "LOG_ERR", + "LOG_FTP", + "LOG_INFO", + "LOG_KERN", + "LOG_LOCAL0", + "LOG_LOCAL1", + "LOG_LOCAL2", + "LOG_LOCAL3", + "LOG_LOCAL4", + "LOG_LOCAL5", + "LOG_LOCAL6", + "LOG_LOCAL7", + "LOG_LPR", + "LOG_MAIL", + "LOG_NEWS", + "LOG_NOTICE", + "LOG_SYSLOG", + "LOG_USER", + "LOG_UUCP", + "LOG_WARNING", + "New", + "NewLogger", + "Priority", + "Writer", + }, + "math": { + "Abs", + "Acos", + "Acosh", + "Asin", + "Asinh", + "Atan", + "Atan2", + "Atanh", + "Cbrt", + "Ceil", + "Copysign", + "Cos", + "Cosh", + "Dim", + "E", + "Erf", + "Erfc", + "Erfcinv", + "Erfinv", + "Exp", + "Exp2", + "Expm1", + "FMA", + "Float32bits", + "Float32frombits", + "Float64bits", + "Float64frombits", + "Floor", + "Frexp", + "Gamma", + "Hypot", + "Ilogb", + "Inf", + "IsInf", + "IsNaN", + "J0", + "J1", + "Jn", + "Ldexp", + "Lgamma", + "Ln10", + "Ln2", + "Log", + "Log10", + "Log10E", + "Log1p", + "Log2", + "Log2E", + "Logb", + "Max", + "MaxFloat32", + "MaxFloat64", + "MaxInt", + "MaxInt16", + "MaxInt32", + "MaxInt64", + "MaxInt8", + "MaxUint", + "MaxUint16", + "MaxUint32", + "MaxUint64", + "MaxUint8", + "Min", + "MinInt", + "MinInt16", + "MinInt32", + "MinInt64", + "MinInt8", + "Mod", + "Modf", + "NaN", + "Nextafter", + "Nextafter32", + "Phi", + "Pi", + "Pow", + "Pow10", + "Remainder", + "Round", + "RoundToEven", + "Signbit", + "Sin", + "Sincos", + "Sinh", + "SmallestNonzeroFloat32", + "SmallestNonzeroFloat64", + "Sqrt", + "Sqrt2", + "SqrtE", + "SqrtPhi", + "SqrtPi", + "Tan", + "Tanh", + "Trunc", + "Y0", + "Y1", + "Yn", + }, + "math/big": { + "Above", + "Accuracy", + "AwayFromZero", + "Below", + "ErrNaN", + "Exact", + "Float", + "Int", + "Jacobi", + "MaxBase", + "MaxExp", + "MaxPrec", + "MinExp", + "NewFloat", + "NewInt", + "NewRat", + "ParseFloat", + "Rat", + "RoundingMode", + "ToNearestAway", + "ToNearestEven", + "ToNegativeInf", + "ToPositiveInf", + "ToZero", + "Word", + }, + "math/bits": { + "Add", + "Add32", + "Add64", + "Div", + "Div32", + "Div64", + "LeadingZeros", + "LeadingZeros16", + "LeadingZeros32", + "LeadingZeros64", + "LeadingZeros8", + "Len", + "Len16", + "Len32", + "Len64", + "Len8", + "Mul", + "Mul32", + "Mul64", + "OnesCount", + "OnesCount16", + "OnesCount32", + "OnesCount64", + "OnesCount8", + "Rem", + "Rem32", + "Rem64", + "Reverse", + "Reverse16", + "Reverse32", + "Reverse64", + "Reverse8", + "ReverseBytes", + "ReverseBytes16", + "ReverseBytes32", + "ReverseBytes64", + "RotateLeft", + "RotateLeft16", + "RotateLeft32", + "RotateLeft64", + "RotateLeft8", + "Sub", + "Sub32", + "Sub64", + "TrailingZeros", + "TrailingZeros16", + "TrailingZeros32", + "TrailingZeros64", + "TrailingZeros8", + "UintSize", + }, + "math/cmplx": { + "Abs", + "Acos", + "Acosh", + "Asin", + "Asinh", + "Atan", + "Atanh", + "Conj", + "Cos", + "Cosh", + "Cot", + "Exp", + "Inf", + "IsInf", + "IsNaN", + "Log", + "Log10", + "NaN", + "Phase", + "Polar", + "Pow", + "Rect", + "Sin", + "Sinh", + "Sqrt", + "Tan", + "Tanh", + }, + "math/rand": { + "ExpFloat64", + "Float32", + "Float64", + "Int", + "Int31", + "Int31n", + "Int63", + "Int63n", + "Intn", + "New", + "NewSource", + "NewZipf", + "NormFloat64", + "Perm", + "Rand", + "Read", + "Seed", + "Shuffle", + "Source", + "Source64", + "Uint32", + "Uint64", + "Zipf", + }, + "mime": { + "AddExtensionType", + "BEncoding", + "ErrInvalidMediaParameter", + "ExtensionsByType", + "FormatMediaType", + "ParseMediaType", + "QEncoding", + "TypeByExtension", + "WordDecoder", + "WordEncoder", + }, + "mime/multipart": { + "ErrMessageTooLarge", + "File", + "FileHeader", + "Form", + "NewReader", + "NewWriter", + "Part", + "Reader", + "Writer", + }, + "mime/quotedprintable": { + "NewReader", + "NewWriter", + "Reader", + "Writer", + }, + "net": { + "Addr", + "AddrError", + "Buffers", + "CIDRMask", + "Conn", + "DNSConfigError", + "DNSError", + "DefaultResolver", + "Dial", + "DialIP", + "DialTCP", + "DialTimeout", + "DialUDP", + "DialUnix", + "Dialer", + "ErrClosed", + "ErrWriteToConnected", + "Error", + "FileConn", + "FileListener", + "FilePacketConn", + "FlagBroadcast", + "FlagLoopback", + "FlagMulticast", + "FlagPointToPoint", + "FlagUp", + "Flags", + "HardwareAddr", + "IP", + "IPAddr", + "IPConn", + "IPMask", + "IPNet", + "IPv4", + "IPv4Mask", + "IPv4allrouter", + "IPv4allsys", + "IPv4bcast", + "IPv4len", + "IPv4zero", + "IPv6interfacelocalallnodes", + "IPv6len", + "IPv6linklocalallnodes", + "IPv6linklocalallrouters", + "IPv6loopback", + "IPv6unspecified", + "IPv6zero", + "Interface", + "InterfaceAddrs", + "InterfaceByIndex", + "InterfaceByName", + "Interfaces", + "InvalidAddrError", + "JoinHostPort", + "Listen", + "ListenConfig", + "ListenIP", + "ListenMulticastUDP", + "ListenPacket", + "ListenTCP", + "ListenUDP", + "ListenUnix", + "ListenUnixgram", + "Listener", + "LookupAddr", + "LookupCNAME", + "LookupHost", + "LookupIP", + "LookupMX", + "LookupNS", + "LookupPort", + "LookupSRV", + "LookupTXT", + "MX", + "NS", + "OpError", + "PacketConn", + "ParseCIDR", + "ParseError", + "ParseIP", + "ParseMAC", + "Pipe", + "ResolveIPAddr", + "ResolveTCPAddr", + "ResolveUDPAddr", + "ResolveUnixAddr", + "Resolver", + "SRV", + "SplitHostPort", + "TCPAddr", + "TCPAddrFromAddrPort", + "TCPConn", + "TCPListener", + "UDPAddr", + "UDPAddrFromAddrPort", + "UDPConn", + "UnixAddr", + "UnixConn", + "UnixListener", + "UnknownNetworkError", + }, + "net/http": { + "AllowQuerySemicolons", + "CanonicalHeaderKey", + "Client", + "CloseNotifier", + "ConnState", + "Cookie", + "CookieJar", + "DefaultClient", + "DefaultMaxHeaderBytes", + "DefaultMaxIdleConnsPerHost", + "DefaultServeMux", + "DefaultTransport", + "DetectContentType", + "Dir", + "ErrAbortHandler", + "ErrBodyNotAllowed", + "ErrBodyReadAfterClose", + "ErrContentLength", + "ErrHandlerTimeout", + "ErrHeaderTooLong", + "ErrHijacked", + "ErrLineTooLong", + "ErrMissingBoundary", + "ErrMissingContentLength", + "ErrMissingFile", + "ErrNoCookie", + "ErrNoLocation", + "ErrNotMultipart", + "ErrNotSupported", + "ErrServerClosed", + "ErrShortBody", + "ErrSkipAltProtocol", + "ErrUnexpectedTrailer", + "ErrUseLastResponse", + "ErrWriteAfterFlush", + "Error", + "FS", + "File", + "FileServer", + "FileSystem", + "Flusher", + "Get", + "Handle", + "HandleFunc", + "Handler", + "HandlerFunc", + "Head", + "Header", + "Hijacker", + "ListenAndServe", + "ListenAndServeTLS", + "LocalAddrContextKey", + "MaxBytesError", + "MaxBytesHandler", + "MaxBytesReader", + "MethodConnect", + "MethodDelete", + "MethodGet", + "MethodHead", + "MethodOptions", + "MethodPatch", + "MethodPost", + "MethodPut", + "MethodTrace", + "NewFileTransport", + "NewRequest", + "NewRequestWithContext", + "NewServeMux", + "NoBody", + "NotFound", + "NotFoundHandler", + "ParseHTTPVersion", + "ParseTime", + "Post", + "PostForm", + "ProtocolError", + "ProxyFromEnvironment", + "ProxyURL", + "PushOptions", + "Pusher", + "ReadRequest", + "ReadResponse", + "Redirect", + "RedirectHandler", + "Request", + "Response", + "ResponseWriter", + "RoundTripper", + "SameSite", + "SameSiteDefaultMode", + "SameSiteLaxMode", + "SameSiteNoneMode", + "SameSiteStrictMode", + "Serve", + "ServeContent", + "ServeFile", + "ServeMux", + "ServeTLS", + "Server", + "ServerContextKey", + "SetCookie", + "StateActive", + "StateClosed", + "StateHijacked", + "StateIdle", + "StateNew", + "StatusAccepted", + "StatusAlreadyReported", + "StatusBadGateway", + "StatusBadRequest", + "StatusConflict", + "StatusContinue", + "StatusCreated", + "StatusEarlyHints", + "StatusExpectationFailed", + "StatusFailedDependency", + "StatusForbidden", + "StatusFound", + "StatusGatewayTimeout", + "StatusGone", + "StatusHTTPVersionNotSupported", + "StatusIMUsed", + "StatusInsufficientStorage", + "StatusInternalServerError", + "StatusLengthRequired", + "StatusLocked", + "StatusLoopDetected", + "StatusMethodNotAllowed", + "StatusMisdirectedRequest", + "StatusMovedPermanently", + "StatusMultiStatus", + "StatusMultipleChoices", + "StatusNetworkAuthenticationRequired", + "StatusNoContent", + "StatusNonAuthoritativeInfo", + "StatusNotAcceptable", + "StatusNotExtended", + "StatusNotFound", + "StatusNotImplemented", + "StatusNotModified", + "StatusOK", + "StatusPartialContent", + "StatusPaymentRequired", + "StatusPermanentRedirect", + "StatusPreconditionFailed", + "StatusPreconditionRequired", + "StatusProcessing", + "StatusProxyAuthRequired", + "StatusRequestEntityTooLarge", + "StatusRequestHeaderFieldsTooLarge", + "StatusRequestTimeout", + "StatusRequestURITooLong", + "StatusRequestedRangeNotSatisfiable", + "StatusResetContent", + "StatusSeeOther", + "StatusServiceUnavailable", + "StatusSwitchingProtocols", + "StatusTeapot", + "StatusTemporaryRedirect", + "StatusText", + "StatusTooEarly", + "StatusTooManyRequests", + "StatusUnauthorized", + "StatusUnavailableForLegalReasons", + "StatusUnprocessableEntity", + "StatusUnsupportedMediaType", + "StatusUpgradeRequired", + "StatusUseProxy", + "StatusVariantAlsoNegotiates", + "StripPrefix", + "TimeFormat", + "TimeoutHandler", + "TrailerPrefix", + "Transport", + }, + "net/http/cgi": { + "Handler", + "Request", + "RequestFromMap", + "Serve", + }, + "net/http/cookiejar": { + "Jar", + "New", + "Options", + "PublicSuffixList", + }, + "net/http/fcgi": { + "ErrConnClosed", + "ErrRequestAborted", + "ProcessEnv", + "Serve", + }, + "net/http/httptest": { + "DefaultRemoteAddr", + "NewRecorder", + "NewRequest", + "NewServer", + "NewTLSServer", + "NewUnstartedServer", + "ResponseRecorder", + "Server", + }, + "net/http/httptrace": { + "ClientTrace", + "ContextClientTrace", + "DNSDoneInfo", + "DNSStartInfo", + "GotConnInfo", + "WithClientTrace", + "WroteRequestInfo", + }, + "net/http/httputil": { + "BufferPool", + "ClientConn", + "DumpRequest", + "DumpRequestOut", + "DumpResponse", + "ErrClosed", + "ErrLineTooLong", + "ErrPersistEOF", + "ErrPipeline", + "NewChunkedReader", + "NewChunkedWriter", + "NewClientConn", + "NewProxyClientConn", + "NewServerConn", + "NewSingleHostReverseProxy", + "ReverseProxy", + "ServerConn", + }, + "net/http/pprof": { + "Cmdline", + "Handler", + "Index", + "Profile", + "Symbol", + "Trace", + }, + "net/mail": { + "Address", + "AddressParser", + "ErrHeaderNotPresent", + "Header", + "Message", + "ParseAddress", + "ParseAddressList", + "ParseDate", + "ReadMessage", + }, + "net/netip": { + "Addr", + "AddrFrom16", + "AddrFrom4", + "AddrFromSlice", + "AddrPort", + "AddrPortFrom", + "IPv4Unspecified", + "IPv6LinkLocalAllNodes", + "IPv6Unspecified", + "MustParseAddr", + "MustParseAddrPort", + "MustParsePrefix", + "ParseAddr", + "ParseAddrPort", + "ParsePrefix", + "Prefix", + "PrefixFrom", + }, + "net/rpc": { + "Accept", + "Call", + "Client", + "ClientCodec", + "DefaultDebugPath", + "DefaultRPCPath", + "DefaultServer", + "Dial", + "DialHTTP", + "DialHTTPPath", + "ErrShutdown", + "HandleHTTP", + "NewClient", + "NewClientWithCodec", + "NewServer", + "Register", + "RegisterName", + "Request", + "Response", + "ServeCodec", + "ServeConn", + "ServeRequest", + "Server", + "ServerCodec", + "ServerError", + }, + "net/rpc/jsonrpc": { + "Dial", + "NewClient", + "NewClientCodec", + "NewServerCodec", + "ServeConn", + }, + "net/smtp": { + "Auth", + "CRAMMD5Auth", + "Client", + "Dial", + "NewClient", + "PlainAuth", + "SendMail", + "ServerInfo", + }, + "net/textproto": { + "CanonicalMIMEHeaderKey", + "Conn", + "Dial", + "Error", + "MIMEHeader", + "NewConn", + "NewReader", + "NewWriter", + "Pipeline", + "ProtocolError", + "Reader", + "TrimBytes", + "TrimString", + "Writer", + }, + "net/url": { + "Error", + "EscapeError", + "InvalidHostError", + "JoinPath", + "Parse", + "ParseQuery", + "ParseRequestURI", + "PathEscape", + "PathUnescape", + "QueryEscape", + "QueryUnescape", + "URL", + "User", + "UserPassword", + "Userinfo", + "Values", + }, + "os": { + "Args", + "Chdir", + "Chmod", + "Chown", + "Chtimes", + "Clearenv", + "Create", + "CreateTemp", + "DevNull", + "DirEntry", + "DirFS", + "Environ", + "ErrClosed", + "ErrDeadlineExceeded", + "ErrExist", + "ErrInvalid", + "ErrNoDeadline", + "ErrNotExist", + "ErrPermission", + "ErrProcessDone", + "Executable", + "Exit", + "Expand", + "ExpandEnv", + "File", + "FileInfo", + "FileMode", + "FindProcess", + "Getegid", + "Getenv", + "Geteuid", + "Getgid", + "Getgroups", + "Getpagesize", + "Getpid", + "Getppid", + "Getuid", + "Getwd", + "Hostname", + "Interrupt", + "IsExist", + "IsNotExist", + "IsPathSeparator", + "IsPermission", + "IsTimeout", + "Kill", + "Lchown", + "Link", + "LinkError", + "LookupEnv", + "Lstat", + "Mkdir", + "MkdirAll", + "MkdirTemp", + "ModeAppend", + "ModeCharDevice", + "ModeDevice", + "ModeDir", + "ModeExclusive", + "ModeIrregular", + "ModeNamedPipe", + "ModePerm", + "ModeSetgid", + "ModeSetuid", + "ModeSocket", + "ModeSticky", + "ModeSymlink", + "ModeTemporary", + "ModeType", + "NewFile", + "NewSyscallError", + "O_APPEND", + "O_CREATE", + "O_EXCL", + "O_RDONLY", + "O_RDWR", + "O_SYNC", + "O_TRUNC", + "O_WRONLY", + "Open", + "OpenFile", + "PathError", + "PathListSeparator", + "PathSeparator", + "Pipe", + "ProcAttr", + "Process", + "ProcessState", + "ReadDir", + "ReadFile", + "Readlink", + "Remove", + "RemoveAll", + "Rename", + "SEEK_CUR", + "SEEK_END", + "SEEK_SET", + "SameFile", + "Setenv", + "Signal", + "StartProcess", + "Stat", + "Stderr", + "Stdin", + "Stdout", + "Symlink", + "SyscallError", + "TempDir", + "Truncate", + "Unsetenv", + "UserCacheDir", + "UserConfigDir", + "UserHomeDir", + "WriteFile", + }, + "os/exec": { + "Cmd", + "Command", + "CommandContext", + "ErrDot", + "ErrNotFound", + "Error", + "ExitError", + "LookPath", + }, + "os/signal": { + "Ignore", + "Ignored", + "Notify", + "NotifyContext", + "Reset", + "Stop", + }, + "os/user": { + "Current", + "Group", + "Lookup", + "LookupGroup", + "LookupGroupId", + "LookupId", + "UnknownGroupError", + "UnknownGroupIdError", + "UnknownUserError", + "UnknownUserIdError", + "User", + }, + "path": { + "Base", + "Clean", + "Dir", + "ErrBadPattern", + "Ext", + "IsAbs", + "Join", + "Match", + "Split", + }, + "path/filepath": { + "Abs", + "Base", + "Clean", + "Dir", + "ErrBadPattern", + "EvalSymlinks", + "Ext", + "FromSlash", + "Glob", + "HasPrefix", + "IsAbs", + "Join", + "ListSeparator", + "Match", + "Rel", + "Separator", + "SkipDir", + "Split", + "SplitList", + "ToSlash", + "VolumeName", + "Walk", + "WalkDir", + "WalkFunc", + }, + "plugin": { + "Open", + "Plugin", + "Symbol", + }, + "reflect": { + "Append", + "AppendSlice", + "Array", + "ArrayOf", + "Bool", + "BothDir", + "Chan", + "ChanDir", + "ChanOf", + "Complex128", + "Complex64", + "Copy", + "DeepEqual", + "Float32", + "Float64", + "Func", + "FuncOf", + "Indirect", + "Int", + "Int16", + "Int32", + "Int64", + "Int8", + "Interface", + "Invalid", + "Kind", + "MakeChan", + "MakeFunc", + "MakeMap", + "MakeMapWithSize", + "MakeSlice", + "Map", + "MapIter", + "MapOf", + "Method", + "New", + "NewAt", + "Pointer", + "PointerTo", + "Ptr", + "PtrTo", + "RecvDir", + "Select", + "SelectCase", + "SelectDefault", + "SelectDir", + "SelectRecv", + "SelectSend", + "SendDir", + "Slice", + "SliceHeader", + "SliceOf", + "String", + "StringHeader", + "Struct", + "StructField", + "StructOf", + "StructTag", + "Swapper", + "Type", + "TypeOf", + "Uint", + "Uint16", + "Uint32", + "Uint64", + "Uint8", + "Uintptr", + "UnsafePointer", + "Value", + "ValueError", + "ValueOf", + "VisibleFields", + "Zero", + }, + "regexp": { + "Compile", + "CompilePOSIX", + "Match", + "MatchReader", + "MatchString", + "MustCompile", + "MustCompilePOSIX", + "QuoteMeta", + "Regexp", + }, + "regexp/syntax": { + "ClassNL", + "Compile", + "DotNL", + "EmptyBeginLine", + "EmptyBeginText", + "EmptyEndLine", + "EmptyEndText", + "EmptyNoWordBoundary", + "EmptyOp", + "EmptyOpContext", + "EmptyWordBoundary", + "ErrInternalError", + "ErrInvalidCharClass", + "ErrInvalidCharRange", + "ErrInvalidEscape", + "ErrInvalidNamedCapture", + "ErrInvalidPerlOp", + "ErrInvalidRepeatOp", + "ErrInvalidRepeatSize", + "ErrInvalidUTF8", + "ErrMissingBracket", + "ErrMissingParen", + "ErrMissingRepeatArgument", + "ErrNestingDepth", + "ErrTrailingBackslash", + "ErrUnexpectedParen", + "Error", + "ErrorCode", + "Flags", + "FoldCase", + "Inst", + "InstAlt", + "InstAltMatch", + "InstCapture", + "InstEmptyWidth", + "InstFail", + "InstMatch", + "InstNop", + "InstOp", + "InstRune", + "InstRune1", + "InstRuneAny", + "InstRuneAnyNotNL", + "IsWordChar", + "Literal", + "MatchNL", + "NonGreedy", + "OneLine", + "Op", + "OpAlternate", + "OpAnyChar", + "OpAnyCharNotNL", + "OpBeginLine", + "OpBeginText", + "OpCapture", + "OpCharClass", + "OpConcat", + "OpEmptyMatch", + "OpEndLine", + "OpEndText", + "OpLiteral", + "OpNoMatch", + "OpNoWordBoundary", + "OpPlus", + "OpQuest", + "OpRepeat", + "OpStar", + "OpWordBoundary", + "POSIX", + "Parse", + "Perl", + "PerlX", + "Prog", + "Regexp", + "Simple", + "UnicodeGroups", + "WasDollar", + }, + "runtime": { + "BlockProfile", + "BlockProfileRecord", + "Breakpoint", + "CPUProfile", + "Caller", + "Callers", + "CallersFrames", + "Compiler", + "Error", + "Frame", + "Frames", + "Func", + "FuncForPC", + "GC", + "GOARCH", + "GOMAXPROCS", + "GOOS", + "GOROOT", + "Goexit", + "GoroutineProfile", + "Gosched", + "KeepAlive", + "LockOSThread", + "MemProfile", + "MemProfileRate", + "MemProfileRecord", + "MemStats", + "MutexProfile", + "NumCPU", + "NumCgoCall", + "NumGoroutine", + "ReadMemStats", + "ReadTrace", + "SetBlockProfileRate", + "SetCPUProfileRate", + "SetCgoTraceback", + "SetFinalizer", + "SetMutexProfileFraction", + "Stack", + "StackRecord", + "StartTrace", + "StopTrace", + "ThreadCreateProfile", + "TypeAssertionError", + "UnlockOSThread", + "Version", + }, + "runtime/cgo": { + "Handle", + "NewHandle", + }, + "runtime/debug": { + "BuildInfo", + "BuildSetting", + "FreeOSMemory", + "GCStats", + "Module", + "ParseBuildInfo", + "PrintStack", + "ReadBuildInfo", + "ReadGCStats", + "SetGCPercent", + "SetMaxStack", + "SetMaxThreads", + "SetMemoryLimit", + "SetPanicOnFault", + "SetTraceback", + "Stack", + "WriteHeapDump", + }, + "runtime/metrics": { + "All", + "Description", + "Float64Histogram", + "KindBad", + "KindFloat64", + "KindFloat64Histogram", + "KindUint64", + "Read", + "Sample", + "Value", + "ValueKind", + }, + "runtime/pprof": { + "Do", + "ForLabels", + "Label", + "LabelSet", + "Labels", + "Lookup", + "NewProfile", + "Profile", + "Profiles", + "SetGoroutineLabels", + "StartCPUProfile", + "StopCPUProfile", + "WithLabels", + "WriteHeapProfile", + }, + "runtime/trace": { + "IsEnabled", + "Log", + "Logf", + "NewTask", + "Region", + "Start", + "StartRegion", + "Stop", + "Task", + "WithRegion", + }, + "sort": { + "Find", + "Float64Slice", + "Float64s", + "Float64sAreSorted", + "IntSlice", + "Interface", + "Ints", + "IntsAreSorted", + "IsSorted", + "Reverse", + "Search", + "SearchFloat64s", + "SearchInts", + "SearchStrings", + "Slice", + "SliceIsSorted", + "SliceStable", + "Sort", + "Stable", + "StringSlice", + "Strings", + "StringsAreSorted", + }, + "strconv": { + "AppendBool", + "AppendFloat", + "AppendInt", + "AppendQuote", + "AppendQuoteRune", + "AppendQuoteRuneToASCII", + "AppendQuoteRuneToGraphic", + "AppendQuoteToASCII", + "AppendQuoteToGraphic", + "AppendUint", + "Atoi", + "CanBackquote", + "ErrRange", + "ErrSyntax", + "FormatBool", + "FormatComplex", + "FormatFloat", + "FormatInt", + "FormatUint", + "IntSize", + "IsGraphic", + "IsPrint", + "Itoa", + "NumError", + "ParseBool", + "ParseComplex", + "ParseFloat", + "ParseInt", + "ParseUint", + "Quote", + "QuoteRune", + "QuoteRuneToASCII", + "QuoteRuneToGraphic", + "QuoteToASCII", + "QuoteToGraphic", + "QuotedPrefix", + "Unquote", + "UnquoteChar", + }, + "strings": { + "Builder", + "Clone", + "Compare", + "Contains", + "ContainsAny", + "ContainsRune", + "Count", + "Cut", + "EqualFold", + "Fields", + "FieldsFunc", + "HasPrefix", + "HasSuffix", + "Index", + "IndexAny", + "IndexByte", + "IndexFunc", + "IndexRune", + "Join", + "LastIndex", + "LastIndexAny", + "LastIndexByte", + "LastIndexFunc", + "Map", + "NewReader", + "NewReplacer", + "Reader", + "Repeat", + "Replace", + "ReplaceAll", + "Replacer", + "Split", + "SplitAfter", + "SplitAfterN", + "SplitN", + "Title", + "ToLower", + "ToLowerSpecial", + "ToTitle", + "ToTitleSpecial", + "ToUpper", + "ToUpperSpecial", + "ToValidUTF8", + "Trim", + "TrimFunc", + "TrimLeft", + "TrimLeftFunc", + "TrimPrefix", + "TrimRight", + "TrimRightFunc", + "TrimSpace", + "TrimSuffix", + }, + "sync": { + "Cond", + "Locker", + "Map", + "Mutex", + "NewCond", + "Once", + "Pool", + "RWMutex", + "WaitGroup", + }, + "sync/atomic": { + "AddInt32", + "AddInt64", + "AddUint32", + "AddUint64", + "AddUintptr", + "Bool", + "CompareAndSwapInt32", + "CompareAndSwapInt64", + "CompareAndSwapPointer", + "CompareAndSwapUint32", + "CompareAndSwapUint64", + "CompareAndSwapUintptr", + "Int32", + "Int64", + "LoadInt32", + "LoadInt64", + "LoadPointer", + "LoadUint32", + "LoadUint64", + "LoadUintptr", + "Pointer", + "StoreInt32", + "StoreInt64", + "StorePointer", + "StoreUint32", + "StoreUint64", + "StoreUintptr", + "SwapInt32", + "SwapInt64", + "SwapPointer", + "SwapUint32", + "SwapUint64", + "SwapUintptr", + "Uint32", + "Uint64", + "Uintptr", + "Value", + }, + "syscall": { + "AF_ALG", + "AF_APPLETALK", + "AF_ARP", + "AF_ASH", + "AF_ATM", + "AF_ATMPVC", + "AF_ATMSVC", + "AF_AX25", + "AF_BLUETOOTH", + "AF_BRIDGE", + "AF_CAIF", + "AF_CAN", + "AF_CCITT", + "AF_CHAOS", + "AF_CNT", + "AF_COIP", + "AF_DATAKIT", + "AF_DECnet", + "AF_DLI", + "AF_E164", + "AF_ECMA", + "AF_ECONET", + "AF_ENCAP", + "AF_FILE", + "AF_HYLINK", + "AF_IEEE80211", + "AF_IEEE802154", + "AF_IMPLINK", + "AF_INET", + "AF_INET6", + "AF_INET6_SDP", + "AF_INET_SDP", + "AF_IPX", + "AF_IRDA", + "AF_ISDN", + "AF_ISO", + "AF_IUCV", + "AF_KEY", + "AF_LAT", + "AF_LINK", + "AF_LLC", + "AF_LOCAL", + "AF_MAX", + "AF_MPLS", + "AF_NATM", + "AF_NDRV", + "AF_NETBEUI", + "AF_NETBIOS", + "AF_NETGRAPH", + "AF_NETLINK", + "AF_NETROM", + "AF_NS", + "AF_OROUTE", + "AF_OSI", + "AF_PACKET", + "AF_PHONET", + "AF_PPP", + "AF_PPPOX", + "AF_PUP", + "AF_RDS", + "AF_RESERVED_36", + "AF_ROSE", + "AF_ROUTE", + "AF_RXRPC", + "AF_SCLUSTER", + "AF_SECURITY", + "AF_SIP", + "AF_SLOW", + "AF_SNA", + "AF_SYSTEM", + "AF_TIPC", + "AF_UNIX", + "AF_UNSPEC", + "AF_VENDOR00", + "AF_VENDOR01", + "AF_VENDOR02", + "AF_VENDOR03", + "AF_VENDOR04", + "AF_VENDOR05", + "AF_VENDOR06", + "AF_VENDOR07", + "AF_VENDOR08", + "AF_VENDOR09", + "AF_VENDOR10", + "AF_VENDOR11", + "AF_VENDOR12", + "AF_VENDOR13", + "AF_VENDOR14", + "AF_VENDOR15", + "AF_VENDOR16", + "AF_VENDOR17", + "AF_VENDOR18", + "AF_VENDOR19", + "AF_VENDOR20", + "AF_VENDOR21", + "AF_VENDOR22", + "AF_VENDOR23", + "AF_VENDOR24", + "AF_VENDOR25", + "AF_VENDOR26", + "AF_VENDOR27", + "AF_VENDOR28", + "AF_VENDOR29", + "AF_VENDOR30", + "AF_VENDOR31", + "AF_VENDOR32", + "AF_VENDOR33", + "AF_VENDOR34", + "AF_VENDOR35", + "AF_VENDOR36", + "AF_VENDOR37", + "AF_VENDOR38", + "AF_VENDOR39", + "AF_VENDOR40", + "AF_VENDOR41", + "AF_VENDOR42", + "AF_VENDOR43", + "AF_VENDOR44", + "AF_VENDOR45", + "AF_VENDOR46", + "AF_VENDOR47", + "AF_WANPIPE", + "AF_X25", + "AI_CANONNAME", + "AI_NUMERICHOST", + "AI_PASSIVE", + "APPLICATION_ERROR", + "ARPHRD_ADAPT", + "ARPHRD_APPLETLK", + "ARPHRD_ARCNET", + "ARPHRD_ASH", + "ARPHRD_ATM", + "ARPHRD_AX25", + "ARPHRD_BIF", + "ARPHRD_CHAOS", + "ARPHRD_CISCO", + "ARPHRD_CSLIP", + "ARPHRD_CSLIP6", + "ARPHRD_DDCMP", + "ARPHRD_DLCI", + "ARPHRD_ECONET", + "ARPHRD_EETHER", + "ARPHRD_ETHER", + "ARPHRD_EUI64", + "ARPHRD_FCAL", + "ARPHRD_FCFABRIC", + "ARPHRD_FCPL", + "ARPHRD_FCPP", + "ARPHRD_FDDI", + "ARPHRD_FRAD", + "ARPHRD_FRELAY", + "ARPHRD_HDLC", + "ARPHRD_HIPPI", + "ARPHRD_HWX25", + "ARPHRD_IEEE1394", + "ARPHRD_IEEE802", + "ARPHRD_IEEE80211", + "ARPHRD_IEEE80211_PRISM", + "ARPHRD_IEEE80211_RADIOTAP", + "ARPHRD_IEEE802154", + "ARPHRD_IEEE802154_PHY", + "ARPHRD_IEEE802_TR", + "ARPHRD_INFINIBAND", + "ARPHRD_IPDDP", + "ARPHRD_IPGRE", + "ARPHRD_IRDA", + "ARPHRD_LAPB", + "ARPHRD_LOCALTLK", + "ARPHRD_LOOPBACK", + "ARPHRD_METRICOM", + "ARPHRD_NETROM", + "ARPHRD_NONE", + "ARPHRD_PIMREG", + "ARPHRD_PPP", + "ARPHRD_PRONET", + "ARPHRD_RAWHDLC", + "ARPHRD_ROSE", + "ARPHRD_RSRVD", + "ARPHRD_SIT", + "ARPHRD_SKIP", + "ARPHRD_SLIP", + "ARPHRD_SLIP6", + "ARPHRD_STRIP", + "ARPHRD_TUNNEL", + "ARPHRD_TUNNEL6", + "ARPHRD_VOID", + "ARPHRD_X25", + "AUTHTYPE_CLIENT", + "AUTHTYPE_SERVER", + "Accept", + "Accept4", + "AcceptEx", + "Access", + "Acct", + "AddrinfoW", + "Adjtime", + "Adjtimex", + "AllThreadsSyscall", + "AllThreadsSyscall6", + "AttachLsf", + "B0", + "B1000000", + "B110", + "B115200", + "B1152000", + "B1200", + "B134", + "B14400", + "B150", + "B1500000", + "B1800", + "B19200", + "B200", + "B2000000", + "B230400", + "B2400", + "B2500000", + "B28800", + "B300", + "B3000000", + "B3500000", + "B38400", + "B4000000", + "B460800", + "B4800", + "B50", + "B500000", + "B57600", + "B576000", + "B600", + "B7200", + "B75", + "B76800", + "B921600", + "B9600", + "BASE_PROTOCOL", + "BIOCFEEDBACK", + "BIOCFLUSH", + "BIOCGBLEN", + "BIOCGDIRECTION", + "BIOCGDIRFILT", + "BIOCGDLT", + "BIOCGDLTLIST", + "BIOCGETBUFMODE", + "BIOCGETIF", + "BIOCGETZMAX", + "BIOCGFEEDBACK", + "BIOCGFILDROP", + "BIOCGHDRCMPLT", + "BIOCGRSIG", + "BIOCGRTIMEOUT", + "BIOCGSEESENT", + "BIOCGSTATS", + "BIOCGSTATSOLD", + "BIOCGTSTAMP", + "BIOCIMMEDIATE", + "BIOCLOCK", + "BIOCPROMISC", + "BIOCROTZBUF", + "BIOCSBLEN", + "BIOCSDIRECTION", + "BIOCSDIRFILT", + "BIOCSDLT", + "BIOCSETBUFMODE", + "BIOCSETF", + "BIOCSETFNR", + "BIOCSETIF", + "BIOCSETWF", + "BIOCSETZBUF", + "BIOCSFEEDBACK", + "BIOCSFILDROP", + "BIOCSHDRCMPLT", + "BIOCSRSIG", + "BIOCSRTIMEOUT", + "BIOCSSEESENT", + "BIOCSTCPF", + "BIOCSTSTAMP", + "BIOCSUDPF", + "BIOCVERSION", + "BPF_A", + "BPF_ABS", + "BPF_ADD", + "BPF_ALIGNMENT", + "BPF_ALIGNMENT32", + "BPF_ALU", + "BPF_AND", + "BPF_B", + "BPF_BUFMODE_BUFFER", + "BPF_BUFMODE_ZBUF", + "BPF_DFLTBUFSIZE", + "BPF_DIRECTION_IN", + "BPF_DIRECTION_OUT", + "BPF_DIV", + "BPF_H", + "BPF_IMM", + "BPF_IND", + "BPF_JA", + "BPF_JEQ", + "BPF_JGE", + "BPF_JGT", + "BPF_JMP", + "BPF_JSET", + "BPF_K", + "BPF_LD", + "BPF_LDX", + "BPF_LEN", + "BPF_LSH", + "BPF_MAJOR_VERSION", + "BPF_MAXBUFSIZE", + "BPF_MAXINSNS", + "BPF_MEM", + "BPF_MEMWORDS", + "BPF_MINBUFSIZE", + "BPF_MINOR_VERSION", + "BPF_MISC", + "BPF_MSH", + "BPF_MUL", + "BPF_NEG", + "BPF_OR", + "BPF_RELEASE", + "BPF_RET", + "BPF_RSH", + "BPF_ST", + "BPF_STX", + "BPF_SUB", + "BPF_TAX", + "BPF_TXA", + "BPF_T_BINTIME", + "BPF_T_BINTIME_FAST", + "BPF_T_BINTIME_MONOTONIC", + "BPF_T_BINTIME_MONOTONIC_FAST", + "BPF_T_FAST", + "BPF_T_FLAG_MASK", + "BPF_T_FORMAT_MASK", + "BPF_T_MICROTIME", + "BPF_T_MICROTIME_FAST", + "BPF_T_MICROTIME_MONOTONIC", + "BPF_T_MICROTIME_MONOTONIC_FAST", + "BPF_T_MONOTONIC", + "BPF_T_MONOTONIC_FAST", + "BPF_T_NANOTIME", + "BPF_T_NANOTIME_FAST", + "BPF_T_NANOTIME_MONOTONIC", + "BPF_T_NANOTIME_MONOTONIC_FAST", + "BPF_T_NONE", + "BPF_T_NORMAL", + "BPF_W", + "BPF_X", + "BRKINT", + "Bind", + "BindToDevice", + "BpfBuflen", + "BpfDatalink", + "BpfHdr", + "BpfHeadercmpl", + "BpfInsn", + "BpfInterface", + "BpfJump", + "BpfProgram", + "BpfStat", + "BpfStats", + "BpfStmt", + "BpfTimeout", + "BpfTimeval", + "BpfVersion", + "BpfZbuf", + "BpfZbufHeader", + "ByHandleFileInformation", + "BytePtrFromString", + "ByteSliceFromString", + "CCR0_FLUSH", + "CERT_CHAIN_POLICY_AUTHENTICODE", + "CERT_CHAIN_POLICY_AUTHENTICODE_TS", + "CERT_CHAIN_POLICY_BASE", + "CERT_CHAIN_POLICY_BASIC_CONSTRAINTS", + "CERT_CHAIN_POLICY_EV", + "CERT_CHAIN_POLICY_MICROSOFT_ROOT", + "CERT_CHAIN_POLICY_NT_AUTH", + "CERT_CHAIN_POLICY_SSL", + "CERT_E_CN_NO_MATCH", + "CERT_E_EXPIRED", + "CERT_E_PURPOSE", + "CERT_E_ROLE", + "CERT_E_UNTRUSTEDROOT", + "CERT_STORE_ADD_ALWAYS", + "CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG", + "CERT_STORE_PROV_MEMORY", + "CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT", + "CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT", + "CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT", + "CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT", + "CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT", + "CERT_TRUST_INVALID_BASIC_CONSTRAINTS", + "CERT_TRUST_INVALID_EXTENSION", + "CERT_TRUST_INVALID_NAME_CONSTRAINTS", + "CERT_TRUST_INVALID_POLICY_CONSTRAINTS", + "CERT_TRUST_IS_CYCLIC", + "CERT_TRUST_IS_EXPLICIT_DISTRUST", + "CERT_TRUST_IS_NOT_SIGNATURE_VALID", + "CERT_TRUST_IS_NOT_TIME_VALID", + "CERT_TRUST_IS_NOT_VALID_FOR_USAGE", + "CERT_TRUST_IS_OFFLINE_REVOCATION", + "CERT_TRUST_IS_REVOKED", + "CERT_TRUST_IS_UNTRUSTED_ROOT", + "CERT_TRUST_NO_ERROR", + "CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY", + "CERT_TRUST_REVOCATION_STATUS_UNKNOWN", + "CFLUSH", + "CLOCAL", + "CLONE_CHILD_CLEARTID", + "CLONE_CHILD_SETTID", + "CLONE_CSIGNAL", + "CLONE_DETACHED", + "CLONE_FILES", + "CLONE_FS", + "CLONE_IO", + "CLONE_NEWIPC", + "CLONE_NEWNET", + "CLONE_NEWNS", + "CLONE_NEWPID", + "CLONE_NEWUSER", + "CLONE_NEWUTS", + "CLONE_PARENT", + "CLONE_PARENT_SETTID", + "CLONE_PID", + "CLONE_PTRACE", + "CLONE_SETTLS", + "CLONE_SIGHAND", + "CLONE_SYSVSEM", + "CLONE_THREAD", + "CLONE_UNTRACED", + "CLONE_VFORK", + "CLONE_VM", + "CPUID_CFLUSH", + "CREAD", + "CREATE_ALWAYS", + "CREATE_NEW", + "CREATE_NEW_PROCESS_GROUP", + "CREATE_UNICODE_ENVIRONMENT", + "CRYPT_DEFAULT_CONTAINER_OPTIONAL", + "CRYPT_DELETEKEYSET", + "CRYPT_MACHINE_KEYSET", + "CRYPT_NEWKEYSET", + "CRYPT_SILENT", + "CRYPT_VERIFYCONTEXT", + "CS5", + "CS6", + "CS7", + "CS8", + "CSIZE", + "CSTART", + "CSTATUS", + "CSTOP", + "CSTOPB", + "CSUSP", + "CTL_MAXNAME", + "CTL_NET", + "CTL_QUERY", + "CTRL_BREAK_EVENT", + "CTRL_CLOSE_EVENT", + "CTRL_C_EVENT", + "CTRL_LOGOFF_EVENT", + "CTRL_SHUTDOWN_EVENT", + "CancelIo", + "CancelIoEx", + "CertAddCertificateContextToStore", + "CertChainContext", + "CertChainElement", + "CertChainPara", + "CertChainPolicyPara", + "CertChainPolicyStatus", + "CertCloseStore", + "CertContext", + "CertCreateCertificateContext", + "CertEnhKeyUsage", + "CertEnumCertificatesInStore", + "CertFreeCertificateChain", + "CertFreeCertificateContext", + "CertGetCertificateChain", + "CertInfo", + "CertOpenStore", + "CertOpenSystemStore", + "CertRevocationCrlInfo", + "CertRevocationInfo", + "CertSimpleChain", + "CertTrustListInfo", + "CertTrustStatus", + "CertUsageMatch", + "CertVerifyCertificateChainPolicy", + "Chdir", + "CheckBpfVersion", + "Chflags", + "Chmod", + "Chown", + "Chroot", + "Clearenv", + "Close", + "CloseHandle", + "CloseOnExec", + "Closesocket", + "CmsgLen", + "CmsgSpace", + "Cmsghdr", + "CommandLineToArgv", + "ComputerName", + "Conn", + "Connect", + "ConnectEx", + "ConvertSidToStringSid", + "ConvertStringSidToSid", + "CopySid", + "Creat", + "CreateDirectory", + "CreateFile", + "CreateFileMapping", + "CreateHardLink", + "CreateIoCompletionPort", + "CreatePipe", + "CreateProcess", + "CreateProcessAsUser", + "CreateSymbolicLink", + "CreateToolhelp32Snapshot", + "Credential", + "CryptAcquireContext", + "CryptGenRandom", + "CryptReleaseContext", + "DIOCBSFLUSH", + "DIOCOSFPFLUSH", + "DLL", + "DLLError", + "DLT_A429", + "DLT_A653_ICM", + "DLT_AIRONET_HEADER", + "DLT_AOS", + "DLT_APPLE_IP_OVER_IEEE1394", + "DLT_ARCNET", + "DLT_ARCNET_LINUX", + "DLT_ATM_CLIP", + "DLT_ATM_RFC1483", + "DLT_AURORA", + "DLT_AX25", + "DLT_AX25_KISS", + "DLT_BACNET_MS_TP", + "DLT_BLUETOOTH_HCI_H4", + "DLT_BLUETOOTH_HCI_H4_WITH_PHDR", + "DLT_CAN20B", + "DLT_CAN_SOCKETCAN", + "DLT_CHAOS", + "DLT_CHDLC", + "DLT_CISCO_IOS", + "DLT_C_HDLC", + "DLT_C_HDLC_WITH_DIR", + "DLT_DBUS", + "DLT_DECT", + "DLT_DOCSIS", + "DLT_DVB_CI", + "DLT_ECONET", + "DLT_EN10MB", + "DLT_EN3MB", + "DLT_ENC", + "DLT_ERF", + "DLT_ERF_ETH", + "DLT_ERF_POS", + "DLT_FC_2", + "DLT_FC_2_WITH_FRAME_DELIMS", + "DLT_FDDI", + "DLT_FLEXRAY", + "DLT_FRELAY", + "DLT_FRELAY_WITH_DIR", + "DLT_GCOM_SERIAL", + "DLT_GCOM_T1E1", + "DLT_GPF_F", + "DLT_GPF_T", + "DLT_GPRS_LLC", + "DLT_GSMTAP_ABIS", + "DLT_GSMTAP_UM", + "DLT_HDLC", + "DLT_HHDLC", + "DLT_HIPPI", + "DLT_IBM_SN", + "DLT_IBM_SP", + "DLT_IEEE802", + "DLT_IEEE802_11", + "DLT_IEEE802_11_RADIO", + "DLT_IEEE802_11_RADIO_AVS", + "DLT_IEEE802_15_4", + "DLT_IEEE802_15_4_LINUX", + "DLT_IEEE802_15_4_NOFCS", + "DLT_IEEE802_15_4_NONASK_PHY", + "DLT_IEEE802_16_MAC_CPS", + "DLT_IEEE802_16_MAC_CPS_RADIO", + "DLT_IPFILTER", + "DLT_IPMB", + "DLT_IPMB_LINUX", + "DLT_IPNET", + "DLT_IPOIB", + "DLT_IPV4", + "DLT_IPV6", + "DLT_IP_OVER_FC", + "DLT_JUNIPER_ATM1", + "DLT_JUNIPER_ATM2", + "DLT_JUNIPER_ATM_CEMIC", + "DLT_JUNIPER_CHDLC", + "DLT_JUNIPER_ES", + "DLT_JUNIPER_ETHER", + "DLT_JUNIPER_FIBRECHANNEL", + "DLT_JUNIPER_FRELAY", + "DLT_JUNIPER_GGSN", + "DLT_JUNIPER_ISM", + "DLT_JUNIPER_MFR", + "DLT_JUNIPER_MLFR", + "DLT_JUNIPER_MLPPP", + "DLT_JUNIPER_MONITOR", + "DLT_JUNIPER_PIC_PEER", + "DLT_JUNIPER_PPP", + "DLT_JUNIPER_PPPOE", + "DLT_JUNIPER_PPPOE_ATM", + "DLT_JUNIPER_SERVICES", + "DLT_JUNIPER_SRX_E2E", + "DLT_JUNIPER_ST", + "DLT_JUNIPER_VP", + "DLT_JUNIPER_VS", + "DLT_LAPB_WITH_DIR", + "DLT_LAPD", + "DLT_LIN", + "DLT_LINUX_EVDEV", + "DLT_LINUX_IRDA", + "DLT_LINUX_LAPD", + "DLT_LINUX_PPP_WITHDIRECTION", + "DLT_LINUX_SLL", + "DLT_LOOP", + "DLT_LTALK", + "DLT_MATCHING_MAX", + "DLT_MATCHING_MIN", + "DLT_MFR", + "DLT_MOST", + "DLT_MPEG_2_TS", + "DLT_MPLS", + "DLT_MTP2", + "DLT_MTP2_WITH_PHDR", + "DLT_MTP3", + "DLT_MUX27010", + "DLT_NETANALYZER", + "DLT_NETANALYZER_TRANSPARENT", + "DLT_NFC_LLCP", + "DLT_NFLOG", + "DLT_NG40", + "DLT_NULL", + "DLT_PCI_EXP", + "DLT_PFLOG", + "DLT_PFSYNC", + "DLT_PPI", + "DLT_PPP", + "DLT_PPP_BSDOS", + "DLT_PPP_ETHER", + "DLT_PPP_PPPD", + "DLT_PPP_SERIAL", + "DLT_PPP_WITH_DIR", + "DLT_PPP_WITH_DIRECTION", + "DLT_PRISM_HEADER", + "DLT_PRONET", + "DLT_RAIF1", + "DLT_RAW", + "DLT_RAWAF_MASK", + "DLT_RIO", + "DLT_SCCP", + "DLT_SITA", + "DLT_SLIP", + "DLT_SLIP_BSDOS", + "DLT_STANAG_5066_D_PDU", + "DLT_SUNATM", + "DLT_SYMANTEC_FIREWALL", + "DLT_TZSP", + "DLT_USB", + "DLT_USB_LINUX", + "DLT_USB_LINUX_MMAPPED", + "DLT_USER0", + "DLT_USER1", + "DLT_USER10", + "DLT_USER11", + "DLT_USER12", + "DLT_USER13", + "DLT_USER14", + "DLT_USER15", + "DLT_USER2", + "DLT_USER3", + "DLT_USER4", + "DLT_USER5", + "DLT_USER6", + "DLT_USER7", + "DLT_USER8", + "DLT_USER9", + "DLT_WIHART", + "DLT_X2E_SERIAL", + "DLT_X2E_XORAYA", + "DNSMXData", + "DNSPTRData", + "DNSRecord", + "DNSSRVData", + "DNSTXTData", + "DNS_INFO_NO_RECORDS", + "DNS_TYPE_A", + "DNS_TYPE_A6", + "DNS_TYPE_AAAA", + "DNS_TYPE_ADDRS", + "DNS_TYPE_AFSDB", + "DNS_TYPE_ALL", + "DNS_TYPE_ANY", + "DNS_TYPE_ATMA", + "DNS_TYPE_AXFR", + "DNS_TYPE_CERT", + "DNS_TYPE_CNAME", + "DNS_TYPE_DHCID", + "DNS_TYPE_DNAME", + "DNS_TYPE_DNSKEY", + "DNS_TYPE_DS", + "DNS_TYPE_EID", + "DNS_TYPE_GID", + "DNS_TYPE_GPOS", + "DNS_TYPE_HINFO", + "DNS_TYPE_ISDN", + "DNS_TYPE_IXFR", + "DNS_TYPE_KEY", + "DNS_TYPE_KX", + "DNS_TYPE_LOC", + "DNS_TYPE_MAILA", + "DNS_TYPE_MAILB", + "DNS_TYPE_MB", + "DNS_TYPE_MD", + "DNS_TYPE_MF", + "DNS_TYPE_MG", + "DNS_TYPE_MINFO", + "DNS_TYPE_MR", + "DNS_TYPE_MX", + "DNS_TYPE_NAPTR", + "DNS_TYPE_NBSTAT", + "DNS_TYPE_NIMLOC", + "DNS_TYPE_NS", + "DNS_TYPE_NSAP", + "DNS_TYPE_NSAPPTR", + "DNS_TYPE_NSEC", + "DNS_TYPE_NULL", + "DNS_TYPE_NXT", + "DNS_TYPE_OPT", + "DNS_TYPE_PTR", + "DNS_TYPE_PX", + "DNS_TYPE_RP", + "DNS_TYPE_RRSIG", + "DNS_TYPE_RT", + "DNS_TYPE_SIG", + "DNS_TYPE_SINK", + "DNS_TYPE_SOA", + "DNS_TYPE_SRV", + "DNS_TYPE_TEXT", + "DNS_TYPE_TKEY", + "DNS_TYPE_TSIG", + "DNS_TYPE_UID", + "DNS_TYPE_UINFO", + "DNS_TYPE_UNSPEC", + "DNS_TYPE_WINS", + "DNS_TYPE_WINSR", + "DNS_TYPE_WKS", + "DNS_TYPE_X25", + "DT_BLK", + "DT_CHR", + "DT_DIR", + "DT_FIFO", + "DT_LNK", + "DT_REG", + "DT_SOCK", + "DT_UNKNOWN", + "DT_WHT", + "DUPLICATE_CLOSE_SOURCE", + "DUPLICATE_SAME_ACCESS", + "DeleteFile", + "DetachLsf", + "DeviceIoControl", + "Dirent", + "DnsNameCompare", + "DnsQuery", + "DnsRecordListFree", + "DnsSectionAdditional", + "DnsSectionAnswer", + "DnsSectionAuthority", + "DnsSectionQuestion", + "Dup", + "Dup2", + "Dup3", + "DuplicateHandle", + "E2BIG", + "EACCES", + "EADDRINUSE", + "EADDRNOTAVAIL", + "EADV", + "EAFNOSUPPORT", + "EAGAIN", + "EALREADY", + "EAUTH", + "EBADARCH", + "EBADE", + "EBADEXEC", + "EBADF", + "EBADFD", + "EBADMACHO", + "EBADMSG", + "EBADR", + "EBADRPC", + "EBADRQC", + "EBADSLT", + "EBFONT", + "EBUSY", + "ECANCELED", + "ECAPMODE", + "ECHILD", + "ECHO", + "ECHOCTL", + "ECHOE", + "ECHOK", + "ECHOKE", + "ECHONL", + "ECHOPRT", + "ECHRNG", + "ECOMM", + "ECONNABORTED", + "ECONNREFUSED", + "ECONNRESET", + "EDEADLK", + "EDEADLOCK", + "EDESTADDRREQ", + "EDEVERR", + "EDOM", + "EDOOFUS", + "EDOTDOT", + "EDQUOT", + "EEXIST", + "EFAULT", + "EFBIG", + "EFER_LMA", + "EFER_LME", + "EFER_NXE", + "EFER_SCE", + "EFTYPE", + "EHOSTDOWN", + "EHOSTUNREACH", + "EHWPOISON", + "EIDRM", + "EILSEQ", + "EINPROGRESS", + "EINTR", + "EINVAL", + "EIO", + "EIPSEC", + "EISCONN", + "EISDIR", + "EISNAM", + "EKEYEXPIRED", + "EKEYREJECTED", + "EKEYREVOKED", + "EL2HLT", + "EL2NSYNC", + "EL3HLT", + "EL3RST", + "ELAST", + "ELF_NGREG", + "ELF_PRARGSZ", + "ELIBACC", + "ELIBBAD", + "ELIBEXEC", + "ELIBMAX", + "ELIBSCN", + "ELNRNG", + "ELOOP", + "EMEDIUMTYPE", + "EMFILE", + "EMLINK", + "EMSGSIZE", + "EMT_TAGOVF", + "EMULTIHOP", + "EMUL_ENABLED", + "EMUL_LINUX", + "EMUL_LINUX32", + "EMUL_MAXID", + "EMUL_NATIVE", + "ENAMETOOLONG", + "ENAVAIL", + "ENDRUNDISC", + "ENEEDAUTH", + "ENETDOWN", + "ENETRESET", + "ENETUNREACH", + "ENFILE", + "ENOANO", + "ENOATTR", + "ENOBUFS", + "ENOCSI", + "ENODATA", + "ENODEV", + "ENOENT", + "ENOEXEC", + "ENOKEY", + "ENOLCK", + "ENOLINK", + "ENOMEDIUM", + "ENOMEM", + "ENOMSG", + "ENONET", + "ENOPKG", + "ENOPOLICY", + "ENOPROTOOPT", + "ENOSPC", + "ENOSR", + "ENOSTR", + "ENOSYS", + "ENOTBLK", + "ENOTCAPABLE", + "ENOTCONN", + "ENOTDIR", + "ENOTEMPTY", + "ENOTNAM", + "ENOTRECOVERABLE", + "ENOTSOCK", + "ENOTSUP", + "ENOTTY", + "ENOTUNIQ", + "ENXIO", + "EN_SW_CTL_INF", + "EN_SW_CTL_PREC", + "EN_SW_CTL_ROUND", + "EN_SW_DATACHAIN", + "EN_SW_DENORM", + "EN_SW_INVOP", + "EN_SW_OVERFLOW", + "EN_SW_PRECLOSS", + "EN_SW_UNDERFLOW", + "EN_SW_ZERODIV", + "EOPNOTSUPP", + "EOVERFLOW", + "EOWNERDEAD", + "EPERM", + "EPFNOSUPPORT", + "EPIPE", + "EPOLLERR", + "EPOLLET", + "EPOLLHUP", + "EPOLLIN", + "EPOLLMSG", + "EPOLLONESHOT", + "EPOLLOUT", + "EPOLLPRI", + "EPOLLRDBAND", + "EPOLLRDHUP", + "EPOLLRDNORM", + "EPOLLWRBAND", + "EPOLLWRNORM", + "EPOLL_CLOEXEC", + "EPOLL_CTL_ADD", + "EPOLL_CTL_DEL", + "EPOLL_CTL_MOD", + "EPOLL_NONBLOCK", + "EPROCLIM", + "EPROCUNAVAIL", + "EPROGMISMATCH", + "EPROGUNAVAIL", + "EPROTO", + "EPROTONOSUPPORT", + "EPROTOTYPE", + "EPWROFF", + "ERANGE", + "EREMCHG", + "EREMOTE", + "EREMOTEIO", + "ERESTART", + "ERFKILL", + "EROFS", + "ERPCMISMATCH", + "ERROR_ACCESS_DENIED", + "ERROR_ALREADY_EXISTS", + "ERROR_BROKEN_PIPE", + "ERROR_BUFFER_OVERFLOW", + "ERROR_DIR_NOT_EMPTY", + "ERROR_ENVVAR_NOT_FOUND", + "ERROR_FILE_EXISTS", + "ERROR_FILE_NOT_FOUND", + "ERROR_HANDLE_EOF", + "ERROR_INSUFFICIENT_BUFFER", + "ERROR_IO_PENDING", + "ERROR_MOD_NOT_FOUND", + "ERROR_MORE_DATA", + "ERROR_NETNAME_DELETED", + "ERROR_NOT_FOUND", + "ERROR_NO_MORE_FILES", + "ERROR_OPERATION_ABORTED", + "ERROR_PATH_NOT_FOUND", + "ERROR_PRIVILEGE_NOT_HELD", + "ERROR_PROC_NOT_FOUND", + "ESHLIBVERS", + "ESHUTDOWN", + "ESOCKTNOSUPPORT", + "ESPIPE", + "ESRCH", + "ESRMNT", + "ESTALE", + "ESTRPIPE", + "ETHERCAP_JUMBO_MTU", + "ETHERCAP_VLAN_HWTAGGING", + "ETHERCAP_VLAN_MTU", + "ETHERMIN", + "ETHERMTU", + "ETHERMTU_JUMBO", + "ETHERTYPE_8023", + "ETHERTYPE_AARP", + "ETHERTYPE_ACCTON", + "ETHERTYPE_AEONIC", + "ETHERTYPE_ALPHA", + "ETHERTYPE_AMBER", + "ETHERTYPE_AMOEBA", + "ETHERTYPE_AOE", + "ETHERTYPE_APOLLO", + "ETHERTYPE_APOLLODOMAIN", + "ETHERTYPE_APPLETALK", + "ETHERTYPE_APPLITEK", + "ETHERTYPE_ARGONAUT", + "ETHERTYPE_ARP", + "ETHERTYPE_AT", + "ETHERTYPE_ATALK", + "ETHERTYPE_ATOMIC", + "ETHERTYPE_ATT", + "ETHERTYPE_ATTSTANFORD", + "ETHERTYPE_AUTOPHON", + "ETHERTYPE_AXIS", + "ETHERTYPE_BCLOOP", + "ETHERTYPE_BOFL", + "ETHERTYPE_CABLETRON", + "ETHERTYPE_CHAOS", + "ETHERTYPE_COMDESIGN", + "ETHERTYPE_COMPUGRAPHIC", + "ETHERTYPE_COUNTERPOINT", + "ETHERTYPE_CRONUS", + "ETHERTYPE_CRONUSVLN", + "ETHERTYPE_DCA", + "ETHERTYPE_DDE", + "ETHERTYPE_DEBNI", + "ETHERTYPE_DECAM", + "ETHERTYPE_DECCUST", + "ETHERTYPE_DECDIAG", + "ETHERTYPE_DECDNS", + "ETHERTYPE_DECDTS", + "ETHERTYPE_DECEXPER", + "ETHERTYPE_DECLAST", + "ETHERTYPE_DECLTM", + "ETHERTYPE_DECMUMPS", + "ETHERTYPE_DECNETBIOS", + "ETHERTYPE_DELTACON", + "ETHERTYPE_DIDDLE", + "ETHERTYPE_DLOG1", + "ETHERTYPE_DLOG2", + "ETHERTYPE_DN", + "ETHERTYPE_DOGFIGHT", + "ETHERTYPE_DSMD", + "ETHERTYPE_ECMA", + "ETHERTYPE_ENCRYPT", + "ETHERTYPE_ES", + "ETHERTYPE_EXCELAN", + "ETHERTYPE_EXPERDATA", + "ETHERTYPE_FLIP", + "ETHERTYPE_FLOWCONTROL", + "ETHERTYPE_FRARP", + "ETHERTYPE_GENDYN", + "ETHERTYPE_HAYES", + "ETHERTYPE_HIPPI_FP", + "ETHERTYPE_HITACHI", + "ETHERTYPE_HP", + "ETHERTYPE_IEEEPUP", + "ETHERTYPE_IEEEPUPAT", + "ETHERTYPE_IMLBL", + "ETHERTYPE_IMLBLDIAG", + "ETHERTYPE_IP", + "ETHERTYPE_IPAS", + "ETHERTYPE_IPV6", + "ETHERTYPE_IPX", + "ETHERTYPE_IPXNEW", + "ETHERTYPE_KALPANA", + "ETHERTYPE_LANBRIDGE", + "ETHERTYPE_LANPROBE", + "ETHERTYPE_LAT", + "ETHERTYPE_LBACK", + "ETHERTYPE_LITTLE", + "ETHERTYPE_LLDP", + "ETHERTYPE_LOGICRAFT", + "ETHERTYPE_LOOPBACK", + "ETHERTYPE_MATRA", + "ETHERTYPE_MAX", + "ETHERTYPE_MERIT", + "ETHERTYPE_MICP", + "ETHERTYPE_MOPDL", + "ETHERTYPE_MOPRC", + "ETHERTYPE_MOTOROLA", + "ETHERTYPE_MPLS", + "ETHERTYPE_MPLS_MCAST", + "ETHERTYPE_MUMPS", + "ETHERTYPE_NBPCC", + "ETHERTYPE_NBPCLAIM", + "ETHERTYPE_NBPCLREQ", + "ETHERTYPE_NBPCLRSP", + "ETHERTYPE_NBPCREQ", + "ETHERTYPE_NBPCRSP", + "ETHERTYPE_NBPDG", + "ETHERTYPE_NBPDGB", + "ETHERTYPE_NBPDLTE", + "ETHERTYPE_NBPRAR", + "ETHERTYPE_NBPRAS", + "ETHERTYPE_NBPRST", + "ETHERTYPE_NBPSCD", + "ETHERTYPE_NBPVCD", + "ETHERTYPE_NBS", + "ETHERTYPE_NCD", + "ETHERTYPE_NESTAR", + "ETHERTYPE_NETBEUI", + "ETHERTYPE_NOVELL", + "ETHERTYPE_NS", + "ETHERTYPE_NSAT", + "ETHERTYPE_NSCOMPAT", + "ETHERTYPE_NTRAILER", + "ETHERTYPE_OS9", + "ETHERTYPE_OS9NET", + "ETHERTYPE_PACER", + "ETHERTYPE_PAE", + "ETHERTYPE_PCS", + "ETHERTYPE_PLANNING", + "ETHERTYPE_PPP", + "ETHERTYPE_PPPOE", + "ETHERTYPE_PPPOEDISC", + "ETHERTYPE_PRIMENTS", + "ETHERTYPE_PUP", + "ETHERTYPE_PUPAT", + "ETHERTYPE_QINQ", + "ETHERTYPE_RACAL", + "ETHERTYPE_RATIONAL", + "ETHERTYPE_RAWFR", + "ETHERTYPE_RCL", + "ETHERTYPE_RDP", + "ETHERTYPE_RETIX", + "ETHERTYPE_REVARP", + "ETHERTYPE_SCA", + "ETHERTYPE_SECTRA", + "ETHERTYPE_SECUREDATA", + "ETHERTYPE_SGITW", + "ETHERTYPE_SG_BOUNCE", + "ETHERTYPE_SG_DIAG", + "ETHERTYPE_SG_NETGAMES", + "ETHERTYPE_SG_RESV", + "ETHERTYPE_SIMNET", + "ETHERTYPE_SLOW", + "ETHERTYPE_SLOWPROTOCOLS", + "ETHERTYPE_SNA", + "ETHERTYPE_SNMP", + "ETHERTYPE_SONIX", + "ETHERTYPE_SPIDER", + "ETHERTYPE_SPRITE", + "ETHERTYPE_STP", + "ETHERTYPE_TALARIS", + "ETHERTYPE_TALARISMC", + "ETHERTYPE_TCPCOMP", + "ETHERTYPE_TCPSM", + "ETHERTYPE_TEC", + "ETHERTYPE_TIGAN", + "ETHERTYPE_TRAIL", + "ETHERTYPE_TRANSETHER", + "ETHERTYPE_TYMSHARE", + "ETHERTYPE_UBBST", + "ETHERTYPE_UBDEBUG", + "ETHERTYPE_UBDIAGLOOP", + "ETHERTYPE_UBDL", + "ETHERTYPE_UBNIU", + "ETHERTYPE_UBNMC", + "ETHERTYPE_VALID", + "ETHERTYPE_VARIAN", + "ETHERTYPE_VAXELN", + "ETHERTYPE_VEECO", + "ETHERTYPE_VEXP", + "ETHERTYPE_VGLAB", + "ETHERTYPE_VINES", + "ETHERTYPE_VINESECHO", + "ETHERTYPE_VINESLOOP", + "ETHERTYPE_VITAL", + "ETHERTYPE_VLAN", + "ETHERTYPE_VLTLMAN", + "ETHERTYPE_VPROD", + "ETHERTYPE_VURESERVED", + "ETHERTYPE_WATERLOO", + "ETHERTYPE_WELLFLEET", + "ETHERTYPE_X25", + "ETHERTYPE_X75", + "ETHERTYPE_XNSSM", + "ETHERTYPE_XTP", + "ETHER_ADDR_LEN", + "ETHER_ALIGN", + "ETHER_CRC_LEN", + "ETHER_CRC_POLY_BE", + "ETHER_CRC_POLY_LE", + "ETHER_HDR_LEN", + "ETHER_MAX_DIX_LEN", + "ETHER_MAX_LEN", + "ETHER_MAX_LEN_JUMBO", + "ETHER_MIN_LEN", + "ETHER_PPPOE_ENCAP_LEN", + "ETHER_TYPE_LEN", + "ETHER_VLAN_ENCAP_LEN", + "ETH_P_1588", + "ETH_P_8021Q", + "ETH_P_802_2", + "ETH_P_802_3", + "ETH_P_AARP", + "ETH_P_ALL", + "ETH_P_AOE", + "ETH_P_ARCNET", + "ETH_P_ARP", + "ETH_P_ATALK", + "ETH_P_ATMFATE", + "ETH_P_ATMMPOA", + "ETH_P_AX25", + "ETH_P_BPQ", + "ETH_P_CAIF", + "ETH_P_CAN", + "ETH_P_CONTROL", + "ETH_P_CUST", + "ETH_P_DDCMP", + "ETH_P_DEC", + "ETH_P_DIAG", + "ETH_P_DNA_DL", + "ETH_P_DNA_RC", + "ETH_P_DNA_RT", + "ETH_P_DSA", + "ETH_P_ECONET", + "ETH_P_EDSA", + "ETH_P_FCOE", + "ETH_P_FIP", + "ETH_P_HDLC", + "ETH_P_IEEE802154", + "ETH_P_IEEEPUP", + "ETH_P_IEEEPUPAT", + "ETH_P_IP", + "ETH_P_IPV6", + "ETH_P_IPX", + "ETH_P_IRDA", + "ETH_P_LAT", + "ETH_P_LINK_CTL", + "ETH_P_LOCALTALK", + "ETH_P_LOOP", + "ETH_P_MOBITEX", + "ETH_P_MPLS_MC", + "ETH_P_MPLS_UC", + "ETH_P_PAE", + "ETH_P_PAUSE", + "ETH_P_PHONET", + "ETH_P_PPPTALK", + "ETH_P_PPP_DISC", + "ETH_P_PPP_MP", + "ETH_P_PPP_SES", + "ETH_P_PUP", + "ETH_P_PUPAT", + "ETH_P_RARP", + "ETH_P_SCA", + "ETH_P_SLOW", + "ETH_P_SNAP", + "ETH_P_TEB", + "ETH_P_TIPC", + "ETH_P_TRAILER", + "ETH_P_TR_802_2", + "ETH_P_WAN_PPP", + "ETH_P_WCCP", + "ETH_P_X25", + "ETIME", + "ETIMEDOUT", + "ETOOMANYREFS", + "ETXTBSY", + "EUCLEAN", + "EUNATCH", + "EUSERS", + "EVFILT_AIO", + "EVFILT_FS", + "EVFILT_LIO", + "EVFILT_MACHPORT", + "EVFILT_PROC", + "EVFILT_READ", + "EVFILT_SIGNAL", + "EVFILT_SYSCOUNT", + "EVFILT_THREADMARKER", + "EVFILT_TIMER", + "EVFILT_USER", + "EVFILT_VM", + "EVFILT_VNODE", + "EVFILT_WRITE", + "EV_ADD", + "EV_CLEAR", + "EV_DELETE", + "EV_DISABLE", + "EV_DISPATCH", + "EV_DROP", + "EV_ENABLE", + "EV_EOF", + "EV_ERROR", + "EV_FLAG0", + "EV_FLAG1", + "EV_ONESHOT", + "EV_OOBAND", + "EV_POLL", + "EV_RECEIPT", + "EV_SYSFLAGS", + "EWINDOWS", + "EWOULDBLOCK", + "EXDEV", + "EXFULL", + "EXTA", + "EXTB", + "EXTPROC", + "Environ", + "EpollCreate", + "EpollCreate1", + "EpollCtl", + "EpollEvent", + "EpollWait", + "Errno", + "EscapeArg", + "Exchangedata", + "Exec", + "Exit", + "ExitProcess", + "FD_CLOEXEC", + "FD_SETSIZE", + "FILE_ACTION_ADDED", + "FILE_ACTION_MODIFIED", + "FILE_ACTION_REMOVED", + "FILE_ACTION_RENAMED_NEW_NAME", + "FILE_ACTION_RENAMED_OLD_NAME", + "FILE_APPEND_DATA", + "FILE_ATTRIBUTE_ARCHIVE", + "FILE_ATTRIBUTE_DIRECTORY", + "FILE_ATTRIBUTE_HIDDEN", + "FILE_ATTRIBUTE_NORMAL", + "FILE_ATTRIBUTE_READONLY", + "FILE_ATTRIBUTE_REPARSE_POINT", + "FILE_ATTRIBUTE_SYSTEM", + "FILE_BEGIN", + "FILE_CURRENT", + "FILE_END", + "FILE_FLAG_BACKUP_SEMANTICS", + "FILE_FLAG_OPEN_REPARSE_POINT", + "FILE_FLAG_OVERLAPPED", + "FILE_LIST_DIRECTORY", + "FILE_MAP_COPY", + "FILE_MAP_EXECUTE", + "FILE_MAP_READ", + "FILE_MAP_WRITE", + "FILE_NOTIFY_CHANGE_ATTRIBUTES", + "FILE_NOTIFY_CHANGE_CREATION", + "FILE_NOTIFY_CHANGE_DIR_NAME", + "FILE_NOTIFY_CHANGE_FILE_NAME", + "FILE_NOTIFY_CHANGE_LAST_ACCESS", + "FILE_NOTIFY_CHANGE_LAST_WRITE", + "FILE_NOTIFY_CHANGE_SIZE", + "FILE_SHARE_DELETE", + "FILE_SHARE_READ", + "FILE_SHARE_WRITE", + "FILE_SKIP_COMPLETION_PORT_ON_SUCCESS", + "FILE_SKIP_SET_EVENT_ON_HANDLE", + "FILE_TYPE_CHAR", + "FILE_TYPE_DISK", + "FILE_TYPE_PIPE", + "FILE_TYPE_REMOTE", + "FILE_TYPE_UNKNOWN", + "FILE_WRITE_ATTRIBUTES", + "FLUSHO", + "FORMAT_MESSAGE_ALLOCATE_BUFFER", + "FORMAT_MESSAGE_ARGUMENT_ARRAY", + "FORMAT_MESSAGE_FROM_HMODULE", + "FORMAT_MESSAGE_FROM_STRING", + "FORMAT_MESSAGE_FROM_SYSTEM", + "FORMAT_MESSAGE_IGNORE_INSERTS", + "FORMAT_MESSAGE_MAX_WIDTH_MASK", + "FSCTL_GET_REPARSE_POINT", + "F_ADDFILESIGS", + "F_ADDSIGS", + "F_ALLOCATEALL", + "F_ALLOCATECONTIG", + "F_CANCEL", + "F_CHKCLEAN", + "F_CLOSEM", + "F_DUP2FD", + "F_DUP2FD_CLOEXEC", + "F_DUPFD", + "F_DUPFD_CLOEXEC", + "F_EXLCK", + "F_FLUSH_DATA", + "F_FREEZE_FS", + "F_FSCTL", + "F_FSDIRMASK", + "F_FSIN", + "F_FSINOUT", + "F_FSOUT", + "F_FSPRIV", + "F_FSVOID", + "F_FULLFSYNC", + "F_GETFD", + "F_GETFL", + "F_GETLEASE", + "F_GETLK", + "F_GETLK64", + "F_GETLKPID", + "F_GETNOSIGPIPE", + "F_GETOWN", + "F_GETOWN_EX", + "F_GETPATH", + "F_GETPATH_MTMINFO", + "F_GETPIPE_SZ", + "F_GETPROTECTIONCLASS", + "F_GETSIG", + "F_GLOBAL_NOCACHE", + "F_LOCK", + "F_LOG2PHYS", + "F_LOG2PHYS_EXT", + "F_MARKDEPENDENCY", + "F_MAXFD", + "F_NOCACHE", + "F_NODIRECT", + "F_NOTIFY", + "F_OGETLK", + "F_OK", + "F_OSETLK", + "F_OSETLKW", + "F_PARAM_MASK", + "F_PARAM_MAX", + "F_PATHPKG_CHECK", + "F_PEOFPOSMODE", + "F_PREALLOCATE", + "F_RDADVISE", + "F_RDAHEAD", + "F_RDLCK", + "F_READAHEAD", + "F_READBOOTSTRAP", + "F_SETBACKINGSTORE", + "F_SETFD", + "F_SETFL", + "F_SETLEASE", + "F_SETLK", + "F_SETLK64", + "F_SETLKW", + "F_SETLKW64", + "F_SETLK_REMOTE", + "F_SETNOSIGPIPE", + "F_SETOWN", + "F_SETOWN_EX", + "F_SETPIPE_SZ", + "F_SETPROTECTIONCLASS", + "F_SETSIG", + "F_SETSIZE", + "F_SHLCK", + "F_TEST", + "F_THAW_FS", + "F_TLOCK", + "F_ULOCK", + "F_UNLCK", + "F_UNLCKSYS", + "F_VOLPOSMODE", + "F_WRITEBOOTSTRAP", + "F_WRLCK", + "Faccessat", + "Fallocate", + "Fbootstraptransfer_t", + "Fchdir", + "Fchflags", + "Fchmod", + "Fchmodat", + "Fchown", + "Fchownat", + "FcntlFlock", + "FdSet", + "Fdatasync", + "FileNotifyInformation", + "Filetime", + "FindClose", + "FindFirstFile", + "FindNextFile", + "Flock", + "Flock_t", + "FlushBpf", + "FlushFileBuffers", + "FlushViewOfFile", + "ForkExec", + "ForkLock", + "FormatMessage", + "Fpathconf", + "FreeAddrInfoW", + "FreeEnvironmentStrings", + "FreeLibrary", + "Fsid", + "Fstat", + "Fstatat", + "Fstatfs", + "Fstore_t", + "Fsync", + "Ftruncate", + "FullPath", + "Futimes", + "Futimesat", + "GENERIC_ALL", + "GENERIC_EXECUTE", + "GENERIC_READ", + "GENERIC_WRITE", + "GUID", + "GetAcceptExSockaddrs", + "GetAdaptersInfo", + "GetAddrInfoW", + "GetCommandLine", + "GetComputerName", + "GetConsoleMode", + "GetCurrentDirectory", + "GetCurrentProcess", + "GetEnvironmentStrings", + "GetEnvironmentVariable", + "GetExitCodeProcess", + "GetFileAttributes", + "GetFileAttributesEx", + "GetFileExInfoStandard", + "GetFileExMaxInfoLevel", + "GetFileInformationByHandle", + "GetFileType", + "GetFullPathName", + "GetHostByName", + "GetIfEntry", + "GetLastError", + "GetLengthSid", + "GetLongPathName", + "GetProcAddress", + "GetProcessTimes", + "GetProtoByName", + "GetQueuedCompletionStatus", + "GetServByName", + "GetShortPathName", + "GetStartupInfo", + "GetStdHandle", + "GetSystemTimeAsFileTime", + "GetTempPath", + "GetTimeZoneInformation", + "GetTokenInformation", + "GetUserNameEx", + "GetUserProfileDirectory", + "GetVersion", + "Getcwd", + "Getdents", + "Getdirentries", + "Getdtablesize", + "Getegid", + "Getenv", + "Geteuid", + "Getfsstat", + "Getgid", + "Getgroups", + "Getpagesize", + "Getpeername", + "Getpgid", + "Getpgrp", + "Getpid", + "Getppid", + "Getpriority", + "Getrlimit", + "Getrusage", + "Getsid", + "Getsockname", + "Getsockopt", + "GetsockoptByte", + "GetsockoptICMPv6Filter", + "GetsockoptIPMreq", + "GetsockoptIPMreqn", + "GetsockoptIPv6MTUInfo", + "GetsockoptIPv6Mreq", + "GetsockoptInet4Addr", + "GetsockoptInt", + "GetsockoptUcred", + "Gettid", + "Gettimeofday", + "Getuid", + "Getwd", + "Getxattr", + "HANDLE_FLAG_INHERIT", + "HKEY_CLASSES_ROOT", + "HKEY_CURRENT_CONFIG", + "HKEY_CURRENT_USER", + "HKEY_DYN_DATA", + "HKEY_LOCAL_MACHINE", + "HKEY_PERFORMANCE_DATA", + "HKEY_USERS", + "HUPCL", + "Handle", + "Hostent", + "ICANON", + "ICMP6_FILTER", + "ICMPV6_FILTER", + "ICMPv6Filter", + "ICRNL", + "IEXTEN", + "IFAN_ARRIVAL", + "IFAN_DEPARTURE", + "IFA_ADDRESS", + "IFA_ANYCAST", + "IFA_BROADCAST", + "IFA_CACHEINFO", + "IFA_F_DADFAILED", + "IFA_F_DEPRECATED", + "IFA_F_HOMEADDRESS", + "IFA_F_NODAD", + "IFA_F_OPTIMISTIC", + "IFA_F_PERMANENT", + "IFA_F_SECONDARY", + "IFA_F_TEMPORARY", + "IFA_F_TENTATIVE", + "IFA_LABEL", + "IFA_LOCAL", + "IFA_MAX", + "IFA_MULTICAST", + "IFA_ROUTE", + "IFA_UNSPEC", + "IFF_ALLMULTI", + "IFF_ALTPHYS", + "IFF_AUTOMEDIA", + "IFF_BROADCAST", + "IFF_CANTCHANGE", + "IFF_CANTCONFIG", + "IFF_DEBUG", + "IFF_DRV_OACTIVE", + "IFF_DRV_RUNNING", + "IFF_DYING", + "IFF_DYNAMIC", + "IFF_LINK0", + "IFF_LINK1", + "IFF_LINK2", + "IFF_LOOPBACK", + "IFF_MASTER", + "IFF_MONITOR", + "IFF_MULTICAST", + "IFF_NOARP", + "IFF_NOTRAILERS", + "IFF_NO_PI", + "IFF_OACTIVE", + "IFF_ONE_QUEUE", + "IFF_POINTOPOINT", + "IFF_POINTTOPOINT", + "IFF_PORTSEL", + "IFF_PPROMISC", + "IFF_PROMISC", + "IFF_RENAMING", + "IFF_RUNNING", + "IFF_SIMPLEX", + "IFF_SLAVE", + "IFF_SMART", + "IFF_STATICARP", + "IFF_TAP", + "IFF_TUN", + "IFF_TUN_EXCL", + "IFF_UP", + "IFF_VNET_HDR", + "IFLA_ADDRESS", + "IFLA_BROADCAST", + "IFLA_COST", + "IFLA_IFALIAS", + "IFLA_IFNAME", + "IFLA_LINK", + "IFLA_LINKINFO", + "IFLA_LINKMODE", + "IFLA_MAP", + "IFLA_MASTER", + "IFLA_MAX", + "IFLA_MTU", + "IFLA_NET_NS_PID", + "IFLA_OPERSTATE", + "IFLA_PRIORITY", + "IFLA_PROTINFO", + "IFLA_QDISC", + "IFLA_STATS", + "IFLA_TXQLEN", + "IFLA_UNSPEC", + "IFLA_WEIGHT", + "IFLA_WIRELESS", + "IFNAMSIZ", + "IFT_1822", + "IFT_A12MPPSWITCH", + "IFT_AAL2", + "IFT_AAL5", + "IFT_ADSL", + "IFT_AFLANE8023", + "IFT_AFLANE8025", + "IFT_ARAP", + "IFT_ARCNET", + "IFT_ARCNETPLUS", + "IFT_ASYNC", + "IFT_ATM", + "IFT_ATMDXI", + "IFT_ATMFUNI", + "IFT_ATMIMA", + "IFT_ATMLOGICAL", + "IFT_ATMRADIO", + "IFT_ATMSUBINTERFACE", + "IFT_ATMVCIENDPT", + "IFT_ATMVIRTUAL", + "IFT_BGPPOLICYACCOUNTING", + "IFT_BLUETOOTH", + "IFT_BRIDGE", + "IFT_BSC", + "IFT_CARP", + "IFT_CCTEMUL", + "IFT_CELLULAR", + "IFT_CEPT", + "IFT_CES", + "IFT_CHANNEL", + "IFT_CNR", + "IFT_COFFEE", + "IFT_COMPOSITELINK", + "IFT_DCN", + "IFT_DIGITALPOWERLINE", + "IFT_DIGITALWRAPPEROVERHEADCHANNEL", + "IFT_DLSW", + "IFT_DOCSCABLEDOWNSTREAM", + "IFT_DOCSCABLEMACLAYER", + "IFT_DOCSCABLEUPSTREAM", + "IFT_DOCSCABLEUPSTREAMCHANNEL", + "IFT_DS0", + "IFT_DS0BUNDLE", + "IFT_DS1FDL", + "IFT_DS3", + "IFT_DTM", + "IFT_DUMMY", + "IFT_DVBASILN", + "IFT_DVBASIOUT", + "IFT_DVBRCCDOWNSTREAM", + "IFT_DVBRCCMACLAYER", + "IFT_DVBRCCUPSTREAM", + "IFT_ECONET", + "IFT_ENC", + "IFT_EON", + "IFT_EPLRS", + "IFT_ESCON", + "IFT_ETHER", + "IFT_FAITH", + "IFT_FAST", + "IFT_FASTETHER", + "IFT_FASTETHERFX", + "IFT_FDDI", + "IFT_FIBRECHANNEL", + "IFT_FRAMERELAYINTERCONNECT", + "IFT_FRAMERELAYMPI", + "IFT_FRDLCIENDPT", + "IFT_FRELAY", + "IFT_FRELAYDCE", + "IFT_FRF16MFRBUNDLE", + "IFT_FRFORWARD", + "IFT_G703AT2MB", + "IFT_G703AT64K", + "IFT_GIF", + "IFT_GIGABITETHERNET", + "IFT_GR303IDT", + "IFT_GR303RDT", + "IFT_H323GATEKEEPER", + "IFT_H323PROXY", + "IFT_HDH1822", + "IFT_HDLC", + "IFT_HDSL2", + "IFT_HIPERLAN2", + "IFT_HIPPI", + "IFT_HIPPIINTERFACE", + "IFT_HOSTPAD", + "IFT_HSSI", + "IFT_HY", + "IFT_IBM370PARCHAN", + "IFT_IDSL", + "IFT_IEEE1394", + "IFT_IEEE80211", + "IFT_IEEE80212", + "IFT_IEEE8023ADLAG", + "IFT_IFGSN", + "IFT_IMT", + "IFT_INFINIBAND", + "IFT_INTERLEAVE", + "IFT_IP", + "IFT_IPFORWARD", + "IFT_IPOVERATM", + "IFT_IPOVERCDLC", + "IFT_IPOVERCLAW", + "IFT_IPSWITCH", + "IFT_IPXIP", + "IFT_ISDN", + "IFT_ISDNBASIC", + "IFT_ISDNPRIMARY", + "IFT_ISDNS", + "IFT_ISDNU", + "IFT_ISO88022LLC", + "IFT_ISO88023", + "IFT_ISO88024", + "IFT_ISO88025", + "IFT_ISO88025CRFPINT", + "IFT_ISO88025DTR", + "IFT_ISO88025FIBER", + "IFT_ISO88026", + "IFT_ISUP", + "IFT_L2VLAN", + "IFT_L3IPVLAN", + "IFT_L3IPXVLAN", + "IFT_LAPB", + "IFT_LAPD", + "IFT_LAPF", + "IFT_LINEGROUP", + "IFT_LOCALTALK", + "IFT_LOOP", + "IFT_MEDIAMAILOVERIP", + "IFT_MFSIGLINK", + "IFT_MIOX25", + "IFT_MODEM", + "IFT_MPC", + "IFT_MPLS", + "IFT_MPLSTUNNEL", + "IFT_MSDSL", + "IFT_MVL", + "IFT_MYRINET", + "IFT_NFAS", + "IFT_NSIP", + "IFT_OPTICALCHANNEL", + "IFT_OPTICALTRANSPORT", + "IFT_OTHER", + "IFT_P10", + "IFT_P80", + "IFT_PARA", + "IFT_PDP", + "IFT_PFLOG", + "IFT_PFLOW", + "IFT_PFSYNC", + "IFT_PLC", + "IFT_PON155", + "IFT_PON622", + "IFT_POS", + "IFT_PPP", + "IFT_PPPMULTILINKBUNDLE", + "IFT_PROPATM", + "IFT_PROPBWAP2MP", + "IFT_PROPCNLS", + "IFT_PROPDOCSWIRELESSDOWNSTREAM", + "IFT_PROPDOCSWIRELESSMACLAYER", + "IFT_PROPDOCSWIRELESSUPSTREAM", + "IFT_PROPMUX", + "IFT_PROPVIRTUAL", + "IFT_PROPWIRELESSP2P", + "IFT_PTPSERIAL", + "IFT_PVC", + "IFT_Q2931", + "IFT_QLLC", + "IFT_RADIOMAC", + "IFT_RADSL", + "IFT_REACHDSL", + "IFT_RFC1483", + "IFT_RS232", + "IFT_RSRB", + "IFT_SDLC", + "IFT_SDSL", + "IFT_SHDSL", + "IFT_SIP", + "IFT_SIPSIG", + "IFT_SIPTG", + "IFT_SLIP", + "IFT_SMDSDXI", + "IFT_SMDSICIP", + "IFT_SONET", + "IFT_SONETOVERHEADCHANNEL", + "IFT_SONETPATH", + "IFT_SONETVT", + "IFT_SRP", + "IFT_SS7SIGLINK", + "IFT_STACKTOSTACK", + "IFT_STARLAN", + "IFT_STF", + "IFT_T1", + "IFT_TDLC", + "IFT_TELINK", + "IFT_TERMPAD", + "IFT_TR008", + "IFT_TRANSPHDLC", + "IFT_TUNNEL", + "IFT_ULTRA", + "IFT_USB", + "IFT_V11", + "IFT_V35", + "IFT_V36", + "IFT_V37", + "IFT_VDSL", + "IFT_VIRTUALIPADDRESS", + "IFT_VIRTUALTG", + "IFT_VOICEDID", + "IFT_VOICEEM", + "IFT_VOICEEMFGD", + "IFT_VOICEENCAP", + "IFT_VOICEFGDEANA", + "IFT_VOICEFXO", + "IFT_VOICEFXS", + "IFT_VOICEOVERATM", + "IFT_VOICEOVERCABLE", + "IFT_VOICEOVERFRAMERELAY", + "IFT_VOICEOVERIP", + "IFT_X213", + "IFT_X25", + "IFT_X25DDN", + "IFT_X25HUNTGROUP", + "IFT_X25MLP", + "IFT_X25PLE", + "IFT_XETHER", + "IGNBRK", + "IGNCR", + "IGNORE", + "IGNPAR", + "IMAXBEL", + "INFINITE", + "INLCR", + "INPCK", + "INVALID_FILE_ATTRIBUTES", + "IN_ACCESS", + "IN_ALL_EVENTS", + "IN_ATTRIB", + "IN_CLASSA_HOST", + "IN_CLASSA_MAX", + "IN_CLASSA_NET", + "IN_CLASSA_NSHIFT", + "IN_CLASSB_HOST", + "IN_CLASSB_MAX", + "IN_CLASSB_NET", + "IN_CLASSB_NSHIFT", + "IN_CLASSC_HOST", + "IN_CLASSC_NET", + "IN_CLASSC_NSHIFT", + "IN_CLASSD_HOST", + "IN_CLASSD_NET", + "IN_CLASSD_NSHIFT", + "IN_CLOEXEC", + "IN_CLOSE", + "IN_CLOSE_NOWRITE", + "IN_CLOSE_WRITE", + "IN_CREATE", + "IN_DELETE", + "IN_DELETE_SELF", + "IN_DONT_FOLLOW", + "IN_EXCL_UNLINK", + "IN_IGNORED", + "IN_ISDIR", + "IN_LINKLOCALNETNUM", + "IN_LOOPBACKNET", + "IN_MASK_ADD", + "IN_MODIFY", + "IN_MOVE", + "IN_MOVED_FROM", + "IN_MOVED_TO", + "IN_MOVE_SELF", + "IN_NONBLOCK", + "IN_ONESHOT", + "IN_ONLYDIR", + "IN_OPEN", + "IN_Q_OVERFLOW", + "IN_RFC3021_HOST", + "IN_RFC3021_MASK", + "IN_RFC3021_NET", + "IN_RFC3021_NSHIFT", + "IN_UNMOUNT", + "IOC_IN", + "IOC_INOUT", + "IOC_OUT", + "IOC_VENDOR", + "IOC_WS2", + "IO_REPARSE_TAG_SYMLINK", + "IPMreq", + "IPMreqn", + "IPPROTO_3PC", + "IPPROTO_ADFS", + "IPPROTO_AH", + "IPPROTO_AHIP", + "IPPROTO_APES", + "IPPROTO_ARGUS", + "IPPROTO_AX25", + "IPPROTO_BHA", + "IPPROTO_BLT", + "IPPROTO_BRSATMON", + "IPPROTO_CARP", + "IPPROTO_CFTP", + "IPPROTO_CHAOS", + "IPPROTO_CMTP", + "IPPROTO_COMP", + "IPPROTO_CPHB", + "IPPROTO_CPNX", + "IPPROTO_DCCP", + "IPPROTO_DDP", + "IPPROTO_DGP", + "IPPROTO_DIVERT", + "IPPROTO_DIVERT_INIT", + "IPPROTO_DIVERT_RESP", + "IPPROTO_DONE", + "IPPROTO_DSTOPTS", + "IPPROTO_EGP", + "IPPROTO_EMCON", + "IPPROTO_ENCAP", + "IPPROTO_EON", + "IPPROTO_ESP", + "IPPROTO_ETHERIP", + "IPPROTO_FRAGMENT", + "IPPROTO_GGP", + "IPPROTO_GMTP", + "IPPROTO_GRE", + "IPPROTO_HELLO", + "IPPROTO_HMP", + "IPPROTO_HOPOPTS", + "IPPROTO_ICMP", + "IPPROTO_ICMPV6", + "IPPROTO_IDP", + "IPPROTO_IDPR", + "IPPROTO_IDRP", + "IPPROTO_IGMP", + "IPPROTO_IGP", + "IPPROTO_IGRP", + "IPPROTO_IL", + "IPPROTO_INLSP", + "IPPROTO_INP", + "IPPROTO_IP", + "IPPROTO_IPCOMP", + "IPPROTO_IPCV", + "IPPROTO_IPEIP", + "IPPROTO_IPIP", + "IPPROTO_IPPC", + "IPPROTO_IPV4", + "IPPROTO_IPV6", + "IPPROTO_IPV6_ICMP", + "IPPROTO_IRTP", + "IPPROTO_KRYPTOLAN", + "IPPROTO_LARP", + "IPPROTO_LEAF1", + "IPPROTO_LEAF2", + "IPPROTO_MAX", + "IPPROTO_MAXID", + "IPPROTO_MEAS", + "IPPROTO_MH", + "IPPROTO_MHRP", + "IPPROTO_MICP", + "IPPROTO_MOBILE", + "IPPROTO_MPLS", + "IPPROTO_MTP", + "IPPROTO_MUX", + "IPPROTO_ND", + "IPPROTO_NHRP", + "IPPROTO_NONE", + "IPPROTO_NSP", + "IPPROTO_NVPII", + "IPPROTO_OLD_DIVERT", + "IPPROTO_OSPFIGP", + "IPPROTO_PFSYNC", + "IPPROTO_PGM", + "IPPROTO_PIGP", + "IPPROTO_PIM", + "IPPROTO_PRM", + "IPPROTO_PUP", + "IPPROTO_PVP", + "IPPROTO_RAW", + "IPPROTO_RCCMON", + "IPPROTO_RDP", + "IPPROTO_ROUTING", + "IPPROTO_RSVP", + "IPPROTO_RVD", + "IPPROTO_SATEXPAK", + "IPPROTO_SATMON", + "IPPROTO_SCCSP", + "IPPROTO_SCTP", + "IPPROTO_SDRP", + "IPPROTO_SEND", + "IPPROTO_SEP", + "IPPROTO_SKIP", + "IPPROTO_SPACER", + "IPPROTO_SRPC", + "IPPROTO_ST", + "IPPROTO_SVMTP", + "IPPROTO_SWIPE", + "IPPROTO_TCF", + "IPPROTO_TCP", + "IPPROTO_TLSP", + "IPPROTO_TP", + "IPPROTO_TPXX", + "IPPROTO_TRUNK1", + "IPPROTO_TRUNK2", + "IPPROTO_TTP", + "IPPROTO_UDP", + "IPPROTO_UDPLITE", + "IPPROTO_VINES", + "IPPROTO_VISA", + "IPPROTO_VMTP", + "IPPROTO_VRRP", + "IPPROTO_WBEXPAK", + "IPPROTO_WBMON", + "IPPROTO_WSN", + "IPPROTO_XNET", + "IPPROTO_XTP", + "IPV6_2292DSTOPTS", + "IPV6_2292HOPLIMIT", + "IPV6_2292HOPOPTS", + "IPV6_2292NEXTHOP", + "IPV6_2292PKTINFO", + "IPV6_2292PKTOPTIONS", + "IPV6_2292RTHDR", + "IPV6_ADDRFORM", + "IPV6_ADD_MEMBERSHIP", + "IPV6_AUTHHDR", + "IPV6_AUTH_LEVEL", + "IPV6_AUTOFLOWLABEL", + "IPV6_BINDANY", + "IPV6_BINDV6ONLY", + "IPV6_BOUND_IF", + "IPV6_CHECKSUM", + "IPV6_DEFAULT_MULTICAST_HOPS", + "IPV6_DEFAULT_MULTICAST_LOOP", + "IPV6_DEFHLIM", + "IPV6_DONTFRAG", + "IPV6_DROP_MEMBERSHIP", + "IPV6_DSTOPTS", + "IPV6_ESP_NETWORK_LEVEL", + "IPV6_ESP_TRANS_LEVEL", + "IPV6_FAITH", + "IPV6_FLOWINFO_MASK", + "IPV6_FLOWLABEL_MASK", + "IPV6_FRAGTTL", + "IPV6_FW_ADD", + "IPV6_FW_DEL", + "IPV6_FW_FLUSH", + "IPV6_FW_GET", + "IPV6_FW_ZERO", + "IPV6_HLIMDEC", + "IPV6_HOPLIMIT", + "IPV6_HOPOPTS", + "IPV6_IPCOMP_LEVEL", + "IPV6_IPSEC_POLICY", + "IPV6_JOIN_ANYCAST", + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_ANYCAST", + "IPV6_LEAVE_GROUP", + "IPV6_MAXHLIM", + "IPV6_MAXOPTHDR", + "IPV6_MAXPACKET", + "IPV6_MAX_GROUP_SRC_FILTER", + "IPV6_MAX_MEMBERSHIPS", + "IPV6_MAX_SOCK_SRC_FILTER", + "IPV6_MIN_MEMBERSHIPS", + "IPV6_MMTU", + "IPV6_MSFILTER", + "IPV6_MTU", + "IPV6_MTU_DISCOVER", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_NEXTHOP", + "IPV6_OPTIONS", + "IPV6_PATHMTU", + "IPV6_PIPEX", + "IPV6_PKTINFO", + "IPV6_PMTUDISC_DO", + "IPV6_PMTUDISC_DONT", + "IPV6_PMTUDISC_PROBE", + "IPV6_PMTUDISC_WANT", + "IPV6_PORTRANGE", + "IPV6_PORTRANGE_DEFAULT", + "IPV6_PORTRANGE_HIGH", + "IPV6_PORTRANGE_LOW", + "IPV6_PREFER_TEMPADDR", + "IPV6_RECVDSTOPTS", + "IPV6_RECVDSTPORT", + "IPV6_RECVERR", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPATHMTU", + "IPV6_RECVPKTINFO", + "IPV6_RECVRTHDR", + "IPV6_RECVTCLASS", + "IPV6_ROUTER_ALERT", + "IPV6_RTABLE", + "IPV6_RTHDR", + "IPV6_RTHDRDSTOPTS", + "IPV6_RTHDR_LOOSE", + "IPV6_RTHDR_STRICT", + "IPV6_RTHDR_TYPE_0", + "IPV6_RXDSTOPTS", + "IPV6_RXHOPOPTS", + "IPV6_SOCKOPT_RESERVED1", + "IPV6_TCLASS", + "IPV6_UNICAST_HOPS", + "IPV6_USE_MIN_MTU", + "IPV6_V6ONLY", + "IPV6_VERSION", + "IPV6_VERSION_MASK", + "IPV6_XFRM_POLICY", + "IP_ADD_MEMBERSHIP", + "IP_ADD_SOURCE_MEMBERSHIP", + "IP_AUTH_LEVEL", + "IP_BINDANY", + "IP_BLOCK_SOURCE", + "IP_BOUND_IF", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_DF", + "IP_DIVERTFL", + "IP_DONTFRAG", + "IP_DROP_MEMBERSHIP", + "IP_DROP_SOURCE_MEMBERSHIP", + "IP_DUMMYNET3", + "IP_DUMMYNET_CONFIGURE", + "IP_DUMMYNET_DEL", + "IP_DUMMYNET_FLUSH", + "IP_DUMMYNET_GET", + "IP_EF", + "IP_ERRORMTU", + "IP_ESP_NETWORK_LEVEL", + "IP_ESP_TRANS_LEVEL", + "IP_FAITH", + "IP_FREEBIND", + "IP_FW3", + "IP_FW_ADD", + "IP_FW_DEL", + "IP_FW_FLUSH", + "IP_FW_GET", + "IP_FW_NAT_CFG", + "IP_FW_NAT_DEL", + "IP_FW_NAT_GET_CONFIG", + "IP_FW_NAT_GET_LOG", + "IP_FW_RESETLOG", + "IP_FW_TABLE_ADD", + "IP_FW_TABLE_DEL", + "IP_FW_TABLE_FLUSH", + "IP_FW_TABLE_GETSIZE", + "IP_FW_TABLE_LIST", + "IP_FW_ZERO", + "IP_HDRINCL", + "IP_IPCOMP_LEVEL", + "IP_IPSECFLOWINFO", + "IP_IPSEC_LOCAL_AUTH", + "IP_IPSEC_LOCAL_CRED", + "IP_IPSEC_LOCAL_ID", + "IP_IPSEC_POLICY", + "IP_IPSEC_REMOTE_AUTH", + "IP_IPSEC_REMOTE_CRED", + "IP_IPSEC_REMOTE_ID", + "IP_MAXPACKET", + "IP_MAX_GROUP_SRC_FILTER", + "IP_MAX_MEMBERSHIPS", + "IP_MAX_SOCK_MUTE_FILTER", + "IP_MAX_SOCK_SRC_FILTER", + "IP_MAX_SOURCE_FILTER", + "IP_MF", + "IP_MINFRAGSIZE", + "IP_MINTTL", + "IP_MIN_MEMBERSHIPS", + "IP_MSFILTER", + "IP_MSS", + "IP_MTU", + "IP_MTU_DISCOVER", + "IP_MULTICAST_IF", + "IP_MULTICAST_IFINDEX", + "IP_MULTICAST_LOOP", + "IP_MULTICAST_TTL", + "IP_MULTICAST_VIF", + "IP_NAT__XXX", + "IP_OFFMASK", + "IP_OLD_FW_ADD", + "IP_OLD_FW_DEL", + "IP_OLD_FW_FLUSH", + "IP_OLD_FW_GET", + "IP_OLD_FW_RESETLOG", + "IP_OLD_FW_ZERO", + "IP_ONESBCAST", + "IP_OPTIONS", + "IP_ORIGDSTADDR", + "IP_PASSSEC", + "IP_PIPEX", + "IP_PKTINFO", + "IP_PKTOPTIONS", + "IP_PMTUDISC", + "IP_PMTUDISC_DO", + "IP_PMTUDISC_DONT", + "IP_PMTUDISC_PROBE", + "IP_PMTUDISC_WANT", + "IP_PORTRANGE", + "IP_PORTRANGE_DEFAULT", + "IP_PORTRANGE_HIGH", + "IP_PORTRANGE_LOW", + "IP_RECVDSTADDR", + "IP_RECVDSTPORT", + "IP_RECVERR", + "IP_RECVIF", + "IP_RECVOPTS", + "IP_RECVORIGDSTADDR", + "IP_RECVPKTINFO", + "IP_RECVRETOPTS", + "IP_RECVRTABLE", + "IP_RECVTOS", + "IP_RECVTTL", + "IP_RETOPTS", + "IP_RF", + "IP_ROUTER_ALERT", + "IP_RSVP_OFF", + "IP_RSVP_ON", + "IP_RSVP_VIF_OFF", + "IP_RSVP_VIF_ON", + "IP_RTABLE", + "IP_SENDSRCADDR", + "IP_STRIPHDR", + "IP_TOS", + "IP_TRAFFIC_MGT_BACKGROUND", + "IP_TRANSPARENT", + "IP_TTL", + "IP_UNBLOCK_SOURCE", + "IP_XFRM_POLICY", + "IPv6MTUInfo", + "IPv6Mreq", + "ISIG", + "ISTRIP", + "IUCLC", + "IUTF8", + "IXANY", + "IXOFF", + "IXON", + "IfAddrmsg", + "IfAnnounceMsghdr", + "IfData", + "IfInfomsg", + "IfMsghdr", + "IfaMsghdr", + "IfmaMsghdr", + "IfmaMsghdr2", + "ImplementsGetwd", + "Inet4Pktinfo", + "Inet6Pktinfo", + "InotifyAddWatch", + "InotifyEvent", + "InotifyInit", + "InotifyInit1", + "InotifyRmWatch", + "InterfaceAddrMessage", + "InterfaceAnnounceMessage", + "InterfaceInfo", + "InterfaceMessage", + "InterfaceMulticastAddrMessage", + "InvalidHandle", + "Ioperm", + "Iopl", + "Iovec", + "IpAdapterInfo", + "IpAddrString", + "IpAddressString", + "IpMaskString", + "Issetugid", + "KEY_ALL_ACCESS", + "KEY_CREATE_LINK", + "KEY_CREATE_SUB_KEY", + "KEY_ENUMERATE_SUB_KEYS", + "KEY_EXECUTE", + "KEY_NOTIFY", + "KEY_QUERY_VALUE", + "KEY_READ", + "KEY_SET_VALUE", + "KEY_WOW64_32KEY", + "KEY_WOW64_64KEY", + "KEY_WRITE", + "Kevent", + "Kevent_t", + "Kill", + "Klogctl", + "Kqueue", + "LANG_ENGLISH", + "LAYERED_PROTOCOL", + "LCNT_OVERLOAD_FLUSH", + "LINUX_REBOOT_CMD_CAD_OFF", + "LINUX_REBOOT_CMD_CAD_ON", + "LINUX_REBOOT_CMD_HALT", + "LINUX_REBOOT_CMD_KEXEC", + "LINUX_REBOOT_CMD_POWER_OFF", + "LINUX_REBOOT_CMD_RESTART", + "LINUX_REBOOT_CMD_RESTART2", + "LINUX_REBOOT_CMD_SW_SUSPEND", + "LINUX_REBOOT_MAGIC1", + "LINUX_REBOOT_MAGIC2", + "LOCK_EX", + "LOCK_NB", + "LOCK_SH", + "LOCK_UN", + "LazyDLL", + "LazyProc", + "Lchown", + "Linger", + "Link", + "Listen", + "Listxattr", + "LoadCancelIoEx", + "LoadConnectEx", + "LoadCreateSymbolicLink", + "LoadDLL", + "LoadGetAddrInfo", + "LoadLibrary", + "LoadSetFileCompletionNotificationModes", + "LocalFree", + "Log2phys_t", + "LookupAccountName", + "LookupAccountSid", + "LookupSID", + "LsfJump", + "LsfSocket", + "LsfStmt", + "Lstat", + "MADV_AUTOSYNC", + "MADV_CAN_REUSE", + "MADV_CORE", + "MADV_DOFORK", + "MADV_DONTFORK", + "MADV_DONTNEED", + "MADV_FREE", + "MADV_FREE_REUSABLE", + "MADV_FREE_REUSE", + "MADV_HUGEPAGE", + "MADV_HWPOISON", + "MADV_MERGEABLE", + "MADV_NOCORE", + "MADV_NOHUGEPAGE", + "MADV_NORMAL", + "MADV_NOSYNC", + "MADV_PROTECT", + "MADV_RANDOM", + "MADV_REMOVE", + "MADV_SEQUENTIAL", + "MADV_SPACEAVAIL", + "MADV_UNMERGEABLE", + "MADV_WILLNEED", + "MADV_ZERO_WIRED_PAGES", + "MAP_32BIT", + "MAP_ALIGNED_SUPER", + "MAP_ALIGNMENT_16MB", + "MAP_ALIGNMENT_1TB", + "MAP_ALIGNMENT_256TB", + "MAP_ALIGNMENT_4GB", + "MAP_ALIGNMENT_64KB", + "MAP_ALIGNMENT_64PB", + "MAP_ALIGNMENT_MASK", + "MAP_ALIGNMENT_SHIFT", + "MAP_ANON", + "MAP_ANONYMOUS", + "MAP_COPY", + "MAP_DENYWRITE", + "MAP_EXECUTABLE", + "MAP_FILE", + "MAP_FIXED", + "MAP_FLAGMASK", + "MAP_GROWSDOWN", + "MAP_HASSEMAPHORE", + "MAP_HUGETLB", + "MAP_INHERIT", + "MAP_INHERIT_COPY", + "MAP_INHERIT_DEFAULT", + "MAP_INHERIT_DONATE_COPY", + "MAP_INHERIT_NONE", + "MAP_INHERIT_SHARE", + "MAP_JIT", + "MAP_LOCKED", + "MAP_NOCACHE", + "MAP_NOCORE", + "MAP_NOEXTEND", + "MAP_NONBLOCK", + "MAP_NORESERVE", + "MAP_NOSYNC", + "MAP_POPULATE", + "MAP_PREFAULT_READ", + "MAP_PRIVATE", + "MAP_RENAME", + "MAP_RESERVED0080", + "MAP_RESERVED0100", + "MAP_SHARED", + "MAP_STACK", + "MAP_TRYFIXED", + "MAP_TYPE", + "MAP_WIRED", + "MAXIMUM_REPARSE_DATA_BUFFER_SIZE", + "MAXLEN_IFDESCR", + "MAXLEN_PHYSADDR", + "MAX_ADAPTER_ADDRESS_LENGTH", + "MAX_ADAPTER_DESCRIPTION_LENGTH", + "MAX_ADAPTER_NAME_LENGTH", + "MAX_COMPUTERNAME_LENGTH", + "MAX_INTERFACE_NAME_LEN", + "MAX_LONG_PATH", + "MAX_PATH", + "MAX_PROTOCOL_CHAIN", + "MCL_CURRENT", + "MCL_FUTURE", + "MNT_DETACH", + "MNT_EXPIRE", + "MNT_FORCE", + "MSG_BCAST", + "MSG_CMSG_CLOEXEC", + "MSG_COMPAT", + "MSG_CONFIRM", + "MSG_CONTROLMBUF", + "MSG_CTRUNC", + "MSG_DONTROUTE", + "MSG_DONTWAIT", + "MSG_EOF", + "MSG_EOR", + "MSG_ERRQUEUE", + "MSG_FASTOPEN", + "MSG_FIN", + "MSG_FLUSH", + "MSG_HAVEMORE", + "MSG_HOLD", + "MSG_IOVUSRSPACE", + "MSG_LENUSRSPACE", + "MSG_MCAST", + "MSG_MORE", + "MSG_NAMEMBUF", + "MSG_NBIO", + "MSG_NEEDSA", + "MSG_NOSIGNAL", + "MSG_NOTIFICATION", + "MSG_OOB", + "MSG_PEEK", + "MSG_PROXY", + "MSG_RCVMORE", + "MSG_RST", + "MSG_SEND", + "MSG_SYN", + "MSG_TRUNC", + "MSG_TRYHARD", + "MSG_USERFLAGS", + "MSG_WAITALL", + "MSG_WAITFORONE", + "MSG_WAITSTREAM", + "MS_ACTIVE", + "MS_ASYNC", + "MS_BIND", + "MS_DEACTIVATE", + "MS_DIRSYNC", + "MS_INVALIDATE", + "MS_I_VERSION", + "MS_KERNMOUNT", + "MS_KILLPAGES", + "MS_MANDLOCK", + "MS_MGC_MSK", + "MS_MGC_VAL", + "MS_MOVE", + "MS_NOATIME", + "MS_NODEV", + "MS_NODIRATIME", + "MS_NOEXEC", + "MS_NOSUID", + "MS_NOUSER", + "MS_POSIXACL", + "MS_PRIVATE", + "MS_RDONLY", + "MS_REC", + "MS_RELATIME", + "MS_REMOUNT", + "MS_RMT_MASK", + "MS_SHARED", + "MS_SILENT", + "MS_SLAVE", + "MS_STRICTATIME", + "MS_SYNC", + "MS_SYNCHRONOUS", + "MS_UNBINDABLE", + "Madvise", + "MapViewOfFile", + "MaxTokenInfoClass", + "Mclpool", + "MibIfRow", + "Mkdir", + "Mkdirat", + "Mkfifo", + "Mknod", + "Mknodat", + "Mlock", + "Mlockall", + "Mmap", + "Mount", + "MoveFile", + "Mprotect", + "Msghdr", + "Munlock", + "Munlockall", + "Munmap", + "MustLoadDLL", + "NAME_MAX", + "NETLINK_ADD_MEMBERSHIP", + "NETLINK_AUDIT", + "NETLINK_BROADCAST_ERROR", + "NETLINK_CONNECTOR", + "NETLINK_DNRTMSG", + "NETLINK_DROP_MEMBERSHIP", + "NETLINK_ECRYPTFS", + "NETLINK_FIB_LOOKUP", + "NETLINK_FIREWALL", + "NETLINK_GENERIC", + "NETLINK_INET_DIAG", + "NETLINK_IP6_FW", + "NETLINK_ISCSI", + "NETLINK_KOBJECT_UEVENT", + "NETLINK_NETFILTER", + "NETLINK_NFLOG", + "NETLINK_NO_ENOBUFS", + "NETLINK_PKTINFO", + "NETLINK_RDMA", + "NETLINK_ROUTE", + "NETLINK_SCSITRANSPORT", + "NETLINK_SELINUX", + "NETLINK_UNUSED", + "NETLINK_USERSOCK", + "NETLINK_XFRM", + "NET_RT_DUMP", + "NET_RT_DUMP2", + "NET_RT_FLAGS", + "NET_RT_IFLIST", + "NET_RT_IFLIST2", + "NET_RT_IFLISTL", + "NET_RT_IFMALIST", + "NET_RT_MAXID", + "NET_RT_OIFLIST", + "NET_RT_OOIFLIST", + "NET_RT_STAT", + "NET_RT_STATS", + "NET_RT_TABLE", + "NET_RT_TRASH", + "NLA_ALIGNTO", + "NLA_F_NESTED", + "NLA_F_NET_BYTEORDER", + "NLA_HDRLEN", + "NLMSG_ALIGNTO", + "NLMSG_DONE", + "NLMSG_ERROR", + "NLMSG_HDRLEN", + "NLMSG_MIN_TYPE", + "NLMSG_NOOP", + "NLMSG_OVERRUN", + "NLM_F_ACK", + "NLM_F_APPEND", + "NLM_F_ATOMIC", + "NLM_F_CREATE", + "NLM_F_DUMP", + "NLM_F_ECHO", + "NLM_F_EXCL", + "NLM_F_MATCH", + "NLM_F_MULTI", + "NLM_F_REPLACE", + "NLM_F_REQUEST", + "NLM_F_ROOT", + "NOFLSH", + "NOTE_ABSOLUTE", + "NOTE_ATTRIB", + "NOTE_CHILD", + "NOTE_DELETE", + "NOTE_EOF", + "NOTE_EXEC", + "NOTE_EXIT", + "NOTE_EXITSTATUS", + "NOTE_EXTEND", + "NOTE_FFAND", + "NOTE_FFCOPY", + "NOTE_FFCTRLMASK", + "NOTE_FFLAGSMASK", + "NOTE_FFNOP", + "NOTE_FFOR", + "NOTE_FORK", + "NOTE_LINK", + "NOTE_LOWAT", + "NOTE_NONE", + "NOTE_NSECONDS", + "NOTE_PCTRLMASK", + "NOTE_PDATAMASK", + "NOTE_REAP", + "NOTE_RENAME", + "NOTE_RESOURCEEND", + "NOTE_REVOKE", + "NOTE_SECONDS", + "NOTE_SIGNAL", + "NOTE_TRACK", + "NOTE_TRACKERR", + "NOTE_TRIGGER", + "NOTE_TRUNCATE", + "NOTE_USECONDS", + "NOTE_VM_ERROR", + "NOTE_VM_PRESSURE", + "NOTE_VM_PRESSURE_SUDDEN_TERMINATE", + "NOTE_VM_PRESSURE_TERMINATE", + "NOTE_WRITE", + "NameCanonical", + "NameCanonicalEx", + "NameDisplay", + "NameDnsDomain", + "NameFullyQualifiedDN", + "NameSamCompatible", + "NameServicePrincipal", + "NameUniqueId", + "NameUnknown", + "NameUserPrincipal", + "Nanosleep", + "NetApiBufferFree", + "NetGetJoinInformation", + "NetSetupDomainName", + "NetSetupUnjoined", + "NetSetupUnknownStatus", + "NetSetupWorkgroupName", + "NetUserGetInfo", + "NetlinkMessage", + "NetlinkRIB", + "NetlinkRouteAttr", + "NetlinkRouteRequest", + "NewCallback", + "NewCallbackCDecl", + "NewLazyDLL", + "NlAttr", + "NlMsgerr", + "NlMsghdr", + "NsecToFiletime", + "NsecToTimespec", + "NsecToTimeval", + "Ntohs", + "OCRNL", + "OFDEL", + "OFILL", + "OFIOGETBMAP", + "OID_PKIX_KP_SERVER_AUTH", + "OID_SERVER_GATED_CRYPTO", + "OID_SGC_NETSCAPE", + "OLCUC", + "ONLCR", + "ONLRET", + "ONOCR", + "ONOEOT", + "OPEN_ALWAYS", + "OPEN_EXISTING", + "OPOST", + "O_ACCMODE", + "O_ALERT", + "O_ALT_IO", + "O_APPEND", + "O_ASYNC", + "O_CLOEXEC", + "O_CREAT", + "O_DIRECT", + "O_DIRECTORY", + "O_DSYNC", + "O_EVTONLY", + "O_EXCL", + "O_EXEC", + "O_EXLOCK", + "O_FSYNC", + "O_LARGEFILE", + "O_NDELAY", + "O_NOATIME", + "O_NOCTTY", + "O_NOFOLLOW", + "O_NONBLOCK", + "O_NOSIGPIPE", + "O_POPUP", + "O_RDONLY", + "O_RDWR", + "O_RSYNC", + "O_SHLOCK", + "O_SYMLINK", + "O_SYNC", + "O_TRUNC", + "O_TTY_INIT", + "O_WRONLY", + "Open", + "OpenCurrentProcessToken", + "OpenProcess", + "OpenProcessToken", + "Openat", + "Overlapped", + "PACKET_ADD_MEMBERSHIP", + "PACKET_BROADCAST", + "PACKET_DROP_MEMBERSHIP", + "PACKET_FASTROUTE", + "PACKET_HOST", + "PACKET_LOOPBACK", + "PACKET_MR_ALLMULTI", + "PACKET_MR_MULTICAST", + "PACKET_MR_PROMISC", + "PACKET_MULTICAST", + "PACKET_OTHERHOST", + "PACKET_OUTGOING", + "PACKET_RECV_OUTPUT", + "PACKET_RX_RING", + "PACKET_STATISTICS", + "PAGE_EXECUTE_READ", + "PAGE_EXECUTE_READWRITE", + "PAGE_EXECUTE_WRITECOPY", + "PAGE_READONLY", + "PAGE_READWRITE", + "PAGE_WRITECOPY", + "PARENB", + "PARMRK", + "PARODD", + "PENDIN", + "PFL_HIDDEN", + "PFL_MATCHES_PROTOCOL_ZERO", + "PFL_MULTIPLE_PROTO_ENTRIES", + "PFL_NETWORKDIRECT_PROVIDER", + "PFL_RECOMMENDED_PROTO_ENTRY", + "PF_FLUSH", + "PKCS_7_ASN_ENCODING", + "PMC5_PIPELINE_FLUSH", + "PRIO_PGRP", + "PRIO_PROCESS", + "PRIO_USER", + "PRI_IOFLUSH", + "PROCESS_QUERY_INFORMATION", + "PROCESS_TERMINATE", + "PROT_EXEC", + "PROT_GROWSDOWN", + "PROT_GROWSUP", + "PROT_NONE", + "PROT_READ", + "PROT_WRITE", + "PROV_DH_SCHANNEL", + "PROV_DSS", + "PROV_DSS_DH", + "PROV_EC_ECDSA_FULL", + "PROV_EC_ECDSA_SIG", + "PROV_EC_ECNRA_FULL", + "PROV_EC_ECNRA_SIG", + "PROV_FORTEZZA", + "PROV_INTEL_SEC", + "PROV_MS_EXCHANGE", + "PROV_REPLACE_OWF", + "PROV_RNG", + "PROV_RSA_AES", + "PROV_RSA_FULL", + "PROV_RSA_SCHANNEL", + "PROV_RSA_SIG", + "PROV_SPYRUS_LYNKS", + "PROV_SSL", + "PR_CAPBSET_DROP", + "PR_CAPBSET_READ", + "PR_CLEAR_SECCOMP_FILTER", + "PR_ENDIAN_BIG", + "PR_ENDIAN_LITTLE", + "PR_ENDIAN_PPC_LITTLE", + "PR_FPEMU_NOPRINT", + "PR_FPEMU_SIGFPE", + "PR_FP_EXC_ASYNC", + "PR_FP_EXC_DISABLED", + "PR_FP_EXC_DIV", + "PR_FP_EXC_INV", + "PR_FP_EXC_NONRECOV", + "PR_FP_EXC_OVF", + "PR_FP_EXC_PRECISE", + "PR_FP_EXC_RES", + "PR_FP_EXC_SW_ENABLE", + "PR_FP_EXC_UND", + "PR_GET_DUMPABLE", + "PR_GET_ENDIAN", + "PR_GET_FPEMU", + "PR_GET_FPEXC", + "PR_GET_KEEPCAPS", + "PR_GET_NAME", + "PR_GET_PDEATHSIG", + "PR_GET_SECCOMP", + "PR_GET_SECCOMP_FILTER", + "PR_GET_SECUREBITS", + "PR_GET_TIMERSLACK", + "PR_GET_TIMING", + "PR_GET_TSC", + "PR_GET_UNALIGN", + "PR_MCE_KILL", + "PR_MCE_KILL_CLEAR", + "PR_MCE_KILL_DEFAULT", + "PR_MCE_KILL_EARLY", + "PR_MCE_KILL_GET", + "PR_MCE_KILL_LATE", + "PR_MCE_KILL_SET", + "PR_SECCOMP_FILTER_EVENT", + "PR_SECCOMP_FILTER_SYSCALL", + "PR_SET_DUMPABLE", + "PR_SET_ENDIAN", + "PR_SET_FPEMU", + "PR_SET_FPEXC", + "PR_SET_KEEPCAPS", + "PR_SET_NAME", + "PR_SET_PDEATHSIG", + "PR_SET_PTRACER", + "PR_SET_SECCOMP", + "PR_SET_SECCOMP_FILTER", + "PR_SET_SECUREBITS", + "PR_SET_TIMERSLACK", + "PR_SET_TIMING", + "PR_SET_TSC", + "PR_SET_UNALIGN", + "PR_TASK_PERF_EVENTS_DISABLE", + "PR_TASK_PERF_EVENTS_ENABLE", + "PR_TIMING_STATISTICAL", + "PR_TIMING_TIMESTAMP", + "PR_TSC_ENABLE", + "PR_TSC_SIGSEGV", + "PR_UNALIGN_NOPRINT", + "PR_UNALIGN_SIGBUS", + "PTRACE_ARCH_PRCTL", + "PTRACE_ATTACH", + "PTRACE_CONT", + "PTRACE_DETACH", + "PTRACE_EVENT_CLONE", + "PTRACE_EVENT_EXEC", + "PTRACE_EVENT_EXIT", + "PTRACE_EVENT_FORK", + "PTRACE_EVENT_VFORK", + "PTRACE_EVENT_VFORK_DONE", + "PTRACE_GETCRUNCHREGS", + "PTRACE_GETEVENTMSG", + "PTRACE_GETFPREGS", + "PTRACE_GETFPXREGS", + "PTRACE_GETHBPREGS", + "PTRACE_GETREGS", + "PTRACE_GETREGSET", + "PTRACE_GETSIGINFO", + "PTRACE_GETVFPREGS", + "PTRACE_GETWMMXREGS", + "PTRACE_GET_THREAD_AREA", + "PTRACE_KILL", + "PTRACE_OLDSETOPTIONS", + "PTRACE_O_MASK", + "PTRACE_O_TRACECLONE", + "PTRACE_O_TRACEEXEC", + "PTRACE_O_TRACEEXIT", + "PTRACE_O_TRACEFORK", + "PTRACE_O_TRACESYSGOOD", + "PTRACE_O_TRACEVFORK", + "PTRACE_O_TRACEVFORKDONE", + "PTRACE_PEEKDATA", + "PTRACE_PEEKTEXT", + "PTRACE_PEEKUSR", + "PTRACE_POKEDATA", + "PTRACE_POKETEXT", + "PTRACE_POKEUSR", + "PTRACE_SETCRUNCHREGS", + "PTRACE_SETFPREGS", + "PTRACE_SETFPXREGS", + "PTRACE_SETHBPREGS", + "PTRACE_SETOPTIONS", + "PTRACE_SETREGS", + "PTRACE_SETREGSET", + "PTRACE_SETSIGINFO", + "PTRACE_SETVFPREGS", + "PTRACE_SETWMMXREGS", + "PTRACE_SET_SYSCALL", + "PTRACE_SET_THREAD_AREA", + "PTRACE_SINGLEBLOCK", + "PTRACE_SINGLESTEP", + "PTRACE_SYSCALL", + "PTRACE_SYSEMU", + "PTRACE_SYSEMU_SINGLESTEP", + "PTRACE_TRACEME", + "PT_ATTACH", + "PT_ATTACHEXC", + "PT_CONTINUE", + "PT_DATA_ADDR", + "PT_DENY_ATTACH", + "PT_DETACH", + "PT_FIRSTMACH", + "PT_FORCEQUOTA", + "PT_KILL", + "PT_MASK", + "PT_READ_D", + "PT_READ_I", + "PT_READ_U", + "PT_SIGEXC", + "PT_STEP", + "PT_TEXT_ADDR", + "PT_TEXT_END_ADDR", + "PT_THUPDATE", + "PT_TRACE_ME", + "PT_WRITE_D", + "PT_WRITE_I", + "PT_WRITE_U", + "ParseDirent", + "ParseNetlinkMessage", + "ParseNetlinkRouteAttr", + "ParseRoutingMessage", + "ParseRoutingSockaddr", + "ParseSocketControlMessage", + "ParseUnixCredentials", + "ParseUnixRights", + "PathMax", + "Pathconf", + "Pause", + "Pipe", + "Pipe2", + "PivotRoot", + "Pointer", + "PostQueuedCompletionStatus", + "Pread", + "Proc", + "ProcAttr", + "Process32First", + "Process32Next", + "ProcessEntry32", + "ProcessInformation", + "Protoent", + "PtraceAttach", + "PtraceCont", + "PtraceDetach", + "PtraceGetEventMsg", + "PtraceGetRegs", + "PtracePeekData", + "PtracePeekText", + "PtracePokeData", + "PtracePokeText", + "PtraceRegs", + "PtraceSetOptions", + "PtraceSetRegs", + "PtraceSingleStep", + "PtraceSyscall", + "Pwrite", + "REG_BINARY", + "REG_DWORD", + "REG_DWORD_BIG_ENDIAN", + "REG_DWORD_LITTLE_ENDIAN", + "REG_EXPAND_SZ", + "REG_FULL_RESOURCE_DESCRIPTOR", + "REG_LINK", + "REG_MULTI_SZ", + "REG_NONE", + "REG_QWORD", + "REG_QWORD_LITTLE_ENDIAN", + "REG_RESOURCE_LIST", + "REG_RESOURCE_REQUIREMENTS_LIST", + "REG_SZ", + "RLIMIT_AS", + "RLIMIT_CORE", + "RLIMIT_CPU", + "RLIMIT_DATA", + "RLIMIT_FSIZE", + "RLIMIT_NOFILE", + "RLIMIT_STACK", + "RLIM_INFINITY", + "RTAX_ADVMSS", + "RTAX_AUTHOR", + "RTAX_BRD", + "RTAX_CWND", + "RTAX_DST", + "RTAX_FEATURES", + "RTAX_FEATURE_ALLFRAG", + "RTAX_FEATURE_ECN", + "RTAX_FEATURE_SACK", + "RTAX_FEATURE_TIMESTAMP", + "RTAX_GATEWAY", + "RTAX_GENMASK", + "RTAX_HOPLIMIT", + "RTAX_IFA", + "RTAX_IFP", + "RTAX_INITCWND", + "RTAX_INITRWND", + "RTAX_LABEL", + "RTAX_LOCK", + "RTAX_MAX", + "RTAX_MTU", + "RTAX_NETMASK", + "RTAX_REORDERING", + "RTAX_RTO_MIN", + "RTAX_RTT", + "RTAX_RTTVAR", + "RTAX_SRC", + "RTAX_SRCMASK", + "RTAX_SSTHRESH", + "RTAX_TAG", + "RTAX_UNSPEC", + "RTAX_WINDOW", + "RTA_ALIGNTO", + "RTA_AUTHOR", + "RTA_BRD", + "RTA_CACHEINFO", + "RTA_DST", + "RTA_FLOW", + "RTA_GATEWAY", + "RTA_GENMASK", + "RTA_IFA", + "RTA_IFP", + "RTA_IIF", + "RTA_LABEL", + "RTA_MAX", + "RTA_METRICS", + "RTA_MULTIPATH", + "RTA_NETMASK", + "RTA_OIF", + "RTA_PREFSRC", + "RTA_PRIORITY", + "RTA_SRC", + "RTA_SRCMASK", + "RTA_TABLE", + "RTA_TAG", + "RTA_UNSPEC", + "RTCF_DIRECTSRC", + "RTCF_DOREDIRECT", + "RTCF_LOG", + "RTCF_MASQ", + "RTCF_NAT", + "RTCF_VALVE", + "RTF_ADDRCLASSMASK", + "RTF_ADDRCONF", + "RTF_ALLONLINK", + "RTF_ANNOUNCE", + "RTF_BLACKHOLE", + "RTF_BROADCAST", + "RTF_CACHE", + "RTF_CLONED", + "RTF_CLONING", + "RTF_CONDEMNED", + "RTF_DEFAULT", + "RTF_DELCLONE", + "RTF_DONE", + "RTF_DYNAMIC", + "RTF_FLOW", + "RTF_FMASK", + "RTF_GATEWAY", + "RTF_GWFLAG_COMPAT", + "RTF_HOST", + "RTF_IFREF", + "RTF_IFSCOPE", + "RTF_INTERFACE", + "RTF_IRTT", + "RTF_LINKRT", + "RTF_LLDATA", + "RTF_LLINFO", + "RTF_LOCAL", + "RTF_MASK", + "RTF_MODIFIED", + "RTF_MPATH", + "RTF_MPLS", + "RTF_MSS", + "RTF_MTU", + "RTF_MULTICAST", + "RTF_NAT", + "RTF_NOFORWARD", + "RTF_NONEXTHOP", + "RTF_NOPMTUDISC", + "RTF_PERMANENT_ARP", + "RTF_PINNED", + "RTF_POLICY", + "RTF_PRCLONING", + "RTF_PROTO1", + "RTF_PROTO2", + "RTF_PROTO3", + "RTF_REINSTATE", + "RTF_REJECT", + "RTF_RNH_LOCKED", + "RTF_SOURCE", + "RTF_SRC", + "RTF_STATIC", + "RTF_STICKY", + "RTF_THROW", + "RTF_TUNNEL", + "RTF_UP", + "RTF_USETRAILERS", + "RTF_WASCLONED", + "RTF_WINDOW", + "RTF_XRESOLVE", + "RTM_ADD", + "RTM_BASE", + "RTM_CHANGE", + "RTM_CHGADDR", + "RTM_DELACTION", + "RTM_DELADDR", + "RTM_DELADDRLABEL", + "RTM_DELETE", + "RTM_DELLINK", + "RTM_DELMADDR", + "RTM_DELNEIGH", + "RTM_DELQDISC", + "RTM_DELROUTE", + "RTM_DELRULE", + "RTM_DELTCLASS", + "RTM_DELTFILTER", + "RTM_DESYNC", + "RTM_F_CLONED", + "RTM_F_EQUALIZE", + "RTM_F_NOTIFY", + "RTM_F_PREFIX", + "RTM_GET", + "RTM_GET2", + "RTM_GETACTION", + "RTM_GETADDR", + "RTM_GETADDRLABEL", + "RTM_GETANYCAST", + "RTM_GETDCB", + "RTM_GETLINK", + "RTM_GETMULTICAST", + "RTM_GETNEIGH", + "RTM_GETNEIGHTBL", + "RTM_GETQDISC", + "RTM_GETROUTE", + "RTM_GETRULE", + "RTM_GETTCLASS", + "RTM_GETTFILTER", + "RTM_IEEE80211", + "RTM_IFANNOUNCE", + "RTM_IFINFO", + "RTM_IFINFO2", + "RTM_LLINFO_UPD", + "RTM_LOCK", + "RTM_LOSING", + "RTM_MAX", + "RTM_MAXSIZE", + "RTM_MISS", + "RTM_NEWACTION", + "RTM_NEWADDR", + "RTM_NEWADDRLABEL", + "RTM_NEWLINK", + "RTM_NEWMADDR", + "RTM_NEWMADDR2", + "RTM_NEWNDUSEROPT", + "RTM_NEWNEIGH", + "RTM_NEWNEIGHTBL", + "RTM_NEWPREFIX", + "RTM_NEWQDISC", + "RTM_NEWROUTE", + "RTM_NEWRULE", + "RTM_NEWTCLASS", + "RTM_NEWTFILTER", + "RTM_NR_FAMILIES", + "RTM_NR_MSGTYPES", + "RTM_OIFINFO", + "RTM_OLDADD", + "RTM_OLDDEL", + "RTM_OOIFINFO", + "RTM_REDIRECT", + "RTM_RESOLVE", + "RTM_RTTUNIT", + "RTM_SETDCB", + "RTM_SETGATE", + "RTM_SETLINK", + "RTM_SETNEIGHTBL", + "RTM_VERSION", + "RTNH_ALIGNTO", + "RTNH_F_DEAD", + "RTNH_F_ONLINK", + "RTNH_F_PERVASIVE", + "RTNLGRP_IPV4_IFADDR", + "RTNLGRP_IPV4_MROUTE", + "RTNLGRP_IPV4_ROUTE", + "RTNLGRP_IPV4_RULE", + "RTNLGRP_IPV6_IFADDR", + "RTNLGRP_IPV6_IFINFO", + "RTNLGRP_IPV6_MROUTE", + "RTNLGRP_IPV6_PREFIX", + "RTNLGRP_IPV6_ROUTE", + "RTNLGRP_IPV6_RULE", + "RTNLGRP_LINK", + "RTNLGRP_ND_USEROPT", + "RTNLGRP_NEIGH", + "RTNLGRP_NONE", + "RTNLGRP_NOTIFY", + "RTNLGRP_TC", + "RTN_ANYCAST", + "RTN_BLACKHOLE", + "RTN_BROADCAST", + "RTN_LOCAL", + "RTN_MAX", + "RTN_MULTICAST", + "RTN_NAT", + "RTN_PROHIBIT", + "RTN_THROW", + "RTN_UNICAST", + "RTN_UNREACHABLE", + "RTN_UNSPEC", + "RTN_XRESOLVE", + "RTPROT_BIRD", + "RTPROT_BOOT", + "RTPROT_DHCP", + "RTPROT_DNROUTED", + "RTPROT_GATED", + "RTPROT_KERNEL", + "RTPROT_MRT", + "RTPROT_NTK", + "RTPROT_RA", + "RTPROT_REDIRECT", + "RTPROT_STATIC", + "RTPROT_UNSPEC", + "RTPROT_XORP", + "RTPROT_ZEBRA", + "RTV_EXPIRE", + "RTV_HOPCOUNT", + "RTV_MTU", + "RTV_RPIPE", + "RTV_RTT", + "RTV_RTTVAR", + "RTV_SPIPE", + "RTV_SSTHRESH", + "RTV_WEIGHT", + "RT_CACHING_CONTEXT", + "RT_CLASS_DEFAULT", + "RT_CLASS_LOCAL", + "RT_CLASS_MAIN", + "RT_CLASS_MAX", + "RT_CLASS_UNSPEC", + "RT_DEFAULT_FIB", + "RT_NORTREF", + "RT_SCOPE_HOST", + "RT_SCOPE_LINK", + "RT_SCOPE_NOWHERE", + "RT_SCOPE_SITE", + "RT_SCOPE_UNIVERSE", + "RT_TABLEID_MAX", + "RT_TABLE_COMPAT", + "RT_TABLE_DEFAULT", + "RT_TABLE_LOCAL", + "RT_TABLE_MAIN", + "RT_TABLE_MAX", + "RT_TABLE_UNSPEC", + "RUSAGE_CHILDREN", + "RUSAGE_SELF", + "RUSAGE_THREAD", + "Radvisory_t", + "RawConn", + "RawSockaddr", + "RawSockaddrAny", + "RawSockaddrDatalink", + "RawSockaddrInet4", + "RawSockaddrInet6", + "RawSockaddrLinklayer", + "RawSockaddrNetlink", + "RawSockaddrUnix", + "RawSyscall", + "RawSyscall6", + "Read", + "ReadConsole", + "ReadDirectoryChanges", + "ReadDirent", + "ReadFile", + "Readlink", + "Reboot", + "Recvfrom", + "Recvmsg", + "RegCloseKey", + "RegEnumKeyEx", + "RegOpenKeyEx", + "RegQueryInfoKey", + "RegQueryValueEx", + "RemoveDirectory", + "Removexattr", + "Rename", + "Renameat", + "Revoke", + "Rlimit", + "Rmdir", + "RouteMessage", + "RouteRIB", + "RoutingMessage", + "RtAttr", + "RtGenmsg", + "RtMetrics", + "RtMsg", + "RtMsghdr", + "RtNexthop", + "Rusage", + "SCM_BINTIME", + "SCM_CREDENTIALS", + "SCM_CREDS", + "SCM_RIGHTS", + "SCM_TIMESTAMP", + "SCM_TIMESTAMPING", + "SCM_TIMESTAMPNS", + "SCM_TIMESTAMP_MONOTONIC", + "SHUT_RD", + "SHUT_RDWR", + "SHUT_WR", + "SID", + "SIDAndAttributes", + "SIGABRT", + "SIGALRM", + "SIGBUS", + "SIGCHLD", + "SIGCLD", + "SIGCONT", + "SIGEMT", + "SIGFPE", + "SIGHUP", + "SIGILL", + "SIGINFO", + "SIGINT", + "SIGIO", + "SIGIOT", + "SIGKILL", + "SIGLIBRT", + "SIGLWP", + "SIGPIPE", + "SIGPOLL", + "SIGPROF", + "SIGPWR", + "SIGQUIT", + "SIGSEGV", + "SIGSTKFLT", + "SIGSTOP", + "SIGSYS", + "SIGTERM", + "SIGTHR", + "SIGTRAP", + "SIGTSTP", + "SIGTTIN", + "SIGTTOU", + "SIGUNUSED", + "SIGURG", + "SIGUSR1", + "SIGUSR2", + "SIGVTALRM", + "SIGWINCH", + "SIGXCPU", + "SIGXFSZ", + "SIOCADDDLCI", + "SIOCADDMULTI", + "SIOCADDRT", + "SIOCAIFADDR", + "SIOCAIFGROUP", + "SIOCALIFADDR", + "SIOCARPIPLL", + "SIOCATMARK", + "SIOCAUTOADDR", + "SIOCAUTONETMASK", + "SIOCBRDGADD", + "SIOCBRDGADDS", + "SIOCBRDGARL", + "SIOCBRDGDADDR", + "SIOCBRDGDEL", + "SIOCBRDGDELS", + "SIOCBRDGFLUSH", + "SIOCBRDGFRL", + "SIOCBRDGGCACHE", + "SIOCBRDGGFD", + "SIOCBRDGGHT", + "SIOCBRDGGIFFLGS", + "SIOCBRDGGMA", + "SIOCBRDGGPARAM", + "SIOCBRDGGPRI", + "SIOCBRDGGRL", + "SIOCBRDGGSIFS", + "SIOCBRDGGTO", + "SIOCBRDGIFS", + "SIOCBRDGRTS", + "SIOCBRDGSADDR", + "SIOCBRDGSCACHE", + "SIOCBRDGSFD", + "SIOCBRDGSHT", + "SIOCBRDGSIFCOST", + "SIOCBRDGSIFFLGS", + "SIOCBRDGSIFPRIO", + "SIOCBRDGSMA", + "SIOCBRDGSPRI", + "SIOCBRDGSPROTO", + "SIOCBRDGSTO", + "SIOCBRDGSTXHC", + "SIOCDARP", + "SIOCDELDLCI", + "SIOCDELMULTI", + "SIOCDELRT", + "SIOCDEVPRIVATE", + "SIOCDIFADDR", + "SIOCDIFGROUP", + "SIOCDIFPHYADDR", + "SIOCDLIFADDR", + "SIOCDRARP", + "SIOCGARP", + "SIOCGDRVSPEC", + "SIOCGETKALIVE", + "SIOCGETLABEL", + "SIOCGETPFLOW", + "SIOCGETPFSYNC", + "SIOCGETSGCNT", + "SIOCGETVIFCNT", + "SIOCGETVLAN", + "SIOCGHIWAT", + "SIOCGIFADDR", + "SIOCGIFADDRPREF", + "SIOCGIFALIAS", + "SIOCGIFALTMTU", + "SIOCGIFASYNCMAP", + "SIOCGIFBOND", + "SIOCGIFBR", + "SIOCGIFBRDADDR", + "SIOCGIFCAP", + "SIOCGIFCONF", + "SIOCGIFCOUNT", + "SIOCGIFDATA", + "SIOCGIFDESCR", + "SIOCGIFDEVMTU", + "SIOCGIFDLT", + "SIOCGIFDSTADDR", + "SIOCGIFENCAP", + "SIOCGIFFIB", + "SIOCGIFFLAGS", + "SIOCGIFGATTR", + "SIOCGIFGENERIC", + "SIOCGIFGMEMB", + "SIOCGIFGROUP", + "SIOCGIFHARDMTU", + "SIOCGIFHWADDR", + "SIOCGIFINDEX", + "SIOCGIFKPI", + "SIOCGIFMAC", + "SIOCGIFMAP", + "SIOCGIFMEDIA", + "SIOCGIFMEM", + "SIOCGIFMETRIC", + "SIOCGIFMTU", + "SIOCGIFNAME", + "SIOCGIFNETMASK", + "SIOCGIFPDSTADDR", + "SIOCGIFPFLAGS", + "SIOCGIFPHYS", + "SIOCGIFPRIORITY", + "SIOCGIFPSRCADDR", + "SIOCGIFRDOMAIN", + "SIOCGIFRTLABEL", + "SIOCGIFSLAVE", + "SIOCGIFSTATUS", + "SIOCGIFTIMESLOT", + "SIOCGIFTXQLEN", + "SIOCGIFVLAN", + "SIOCGIFWAKEFLAGS", + "SIOCGIFXFLAGS", + "SIOCGLIFADDR", + "SIOCGLIFPHYADDR", + "SIOCGLIFPHYRTABLE", + "SIOCGLIFPHYTTL", + "SIOCGLINKSTR", + "SIOCGLOWAT", + "SIOCGPGRP", + "SIOCGPRIVATE_0", + "SIOCGPRIVATE_1", + "SIOCGRARP", + "SIOCGSPPPPARAMS", + "SIOCGSTAMP", + "SIOCGSTAMPNS", + "SIOCGVH", + "SIOCGVNETID", + "SIOCIFCREATE", + "SIOCIFCREATE2", + "SIOCIFDESTROY", + "SIOCIFGCLONERS", + "SIOCINITIFADDR", + "SIOCPROTOPRIVATE", + "SIOCRSLVMULTI", + "SIOCRTMSG", + "SIOCSARP", + "SIOCSDRVSPEC", + "SIOCSETKALIVE", + "SIOCSETLABEL", + "SIOCSETPFLOW", + "SIOCSETPFSYNC", + "SIOCSETVLAN", + "SIOCSHIWAT", + "SIOCSIFADDR", + "SIOCSIFADDRPREF", + "SIOCSIFALTMTU", + "SIOCSIFASYNCMAP", + "SIOCSIFBOND", + "SIOCSIFBR", + "SIOCSIFBRDADDR", + "SIOCSIFCAP", + "SIOCSIFDESCR", + "SIOCSIFDSTADDR", + "SIOCSIFENCAP", + "SIOCSIFFIB", + "SIOCSIFFLAGS", + "SIOCSIFGATTR", + "SIOCSIFGENERIC", + "SIOCSIFHWADDR", + "SIOCSIFHWBROADCAST", + "SIOCSIFKPI", + "SIOCSIFLINK", + "SIOCSIFLLADDR", + "SIOCSIFMAC", + "SIOCSIFMAP", + "SIOCSIFMEDIA", + "SIOCSIFMEM", + "SIOCSIFMETRIC", + "SIOCSIFMTU", + "SIOCSIFNAME", + "SIOCSIFNETMASK", + "SIOCSIFPFLAGS", + "SIOCSIFPHYADDR", + "SIOCSIFPHYS", + "SIOCSIFPRIORITY", + "SIOCSIFRDOMAIN", + "SIOCSIFRTLABEL", + "SIOCSIFRVNET", + "SIOCSIFSLAVE", + "SIOCSIFTIMESLOT", + "SIOCSIFTXQLEN", + "SIOCSIFVLAN", + "SIOCSIFVNET", + "SIOCSIFXFLAGS", + "SIOCSLIFPHYADDR", + "SIOCSLIFPHYRTABLE", + "SIOCSLIFPHYTTL", + "SIOCSLINKSTR", + "SIOCSLOWAT", + "SIOCSPGRP", + "SIOCSRARP", + "SIOCSSPPPPARAMS", + "SIOCSVH", + "SIOCSVNETID", + "SIOCZIFDATA", + "SIO_GET_EXTENSION_FUNCTION_POINTER", + "SIO_GET_INTERFACE_LIST", + "SIO_KEEPALIVE_VALS", + "SIO_UDP_CONNRESET", + "SOCK_CLOEXEC", + "SOCK_DCCP", + "SOCK_DGRAM", + "SOCK_FLAGS_MASK", + "SOCK_MAXADDRLEN", + "SOCK_NONBLOCK", + "SOCK_NOSIGPIPE", + "SOCK_PACKET", + "SOCK_RAW", + "SOCK_RDM", + "SOCK_SEQPACKET", + "SOCK_STREAM", + "SOL_AAL", + "SOL_ATM", + "SOL_DECNET", + "SOL_ICMPV6", + "SOL_IP", + "SOL_IPV6", + "SOL_IRDA", + "SOL_PACKET", + "SOL_RAW", + "SOL_SOCKET", + "SOL_TCP", + "SOL_X25", + "SOMAXCONN", + "SO_ACCEPTCONN", + "SO_ACCEPTFILTER", + "SO_ATTACH_FILTER", + "SO_BINDANY", + "SO_BINDTODEVICE", + "SO_BINTIME", + "SO_BROADCAST", + "SO_BSDCOMPAT", + "SO_DEBUG", + "SO_DETACH_FILTER", + "SO_DOMAIN", + "SO_DONTROUTE", + "SO_DONTTRUNC", + "SO_ERROR", + "SO_KEEPALIVE", + "SO_LABEL", + "SO_LINGER", + "SO_LINGER_SEC", + "SO_LISTENINCQLEN", + "SO_LISTENQLEN", + "SO_LISTENQLIMIT", + "SO_MARK", + "SO_NETPROC", + "SO_NKE", + "SO_NOADDRERR", + "SO_NOHEADER", + "SO_NOSIGPIPE", + "SO_NOTIFYCONFLICT", + "SO_NO_CHECK", + "SO_NO_DDP", + "SO_NO_OFFLOAD", + "SO_NP_EXTENSIONS", + "SO_NREAD", + "SO_NWRITE", + "SO_OOBINLINE", + "SO_OVERFLOWED", + "SO_PASSCRED", + "SO_PASSSEC", + "SO_PEERCRED", + "SO_PEERLABEL", + "SO_PEERNAME", + "SO_PEERSEC", + "SO_PRIORITY", + "SO_PROTOCOL", + "SO_PROTOTYPE", + "SO_RANDOMPORT", + "SO_RCVBUF", + "SO_RCVBUFFORCE", + "SO_RCVLOWAT", + "SO_RCVTIMEO", + "SO_RESTRICTIONS", + "SO_RESTRICT_DENYIN", + "SO_RESTRICT_DENYOUT", + "SO_RESTRICT_DENYSET", + "SO_REUSEADDR", + "SO_REUSEPORT", + "SO_REUSESHAREUID", + "SO_RTABLE", + "SO_RXQ_OVFL", + "SO_SECURITY_AUTHENTICATION", + "SO_SECURITY_ENCRYPTION_NETWORK", + "SO_SECURITY_ENCRYPTION_TRANSPORT", + "SO_SETFIB", + "SO_SNDBUF", + "SO_SNDBUFFORCE", + "SO_SNDLOWAT", + "SO_SNDTIMEO", + "SO_SPLICE", + "SO_TIMESTAMP", + "SO_TIMESTAMPING", + "SO_TIMESTAMPNS", + "SO_TIMESTAMP_MONOTONIC", + "SO_TYPE", + "SO_UPCALLCLOSEWAIT", + "SO_UPDATE_ACCEPT_CONTEXT", + "SO_UPDATE_CONNECT_CONTEXT", + "SO_USELOOPBACK", + "SO_USER_COOKIE", + "SO_VENDOR", + "SO_WANTMORE", + "SO_WANTOOBFLAG", + "SSLExtraCertChainPolicyPara", + "STANDARD_RIGHTS_ALL", + "STANDARD_RIGHTS_EXECUTE", + "STANDARD_RIGHTS_READ", + "STANDARD_RIGHTS_REQUIRED", + "STANDARD_RIGHTS_WRITE", + "STARTF_USESHOWWINDOW", + "STARTF_USESTDHANDLES", + "STD_ERROR_HANDLE", + "STD_INPUT_HANDLE", + "STD_OUTPUT_HANDLE", + "SUBLANG_ENGLISH_US", + "SW_FORCEMINIMIZE", + "SW_HIDE", + "SW_MAXIMIZE", + "SW_MINIMIZE", + "SW_NORMAL", + "SW_RESTORE", + "SW_SHOW", + "SW_SHOWDEFAULT", + "SW_SHOWMAXIMIZED", + "SW_SHOWMINIMIZED", + "SW_SHOWMINNOACTIVE", + "SW_SHOWNA", + "SW_SHOWNOACTIVATE", + "SW_SHOWNORMAL", + "SYMBOLIC_LINK_FLAG_DIRECTORY", + "SYNCHRONIZE", + "SYSCTL_VERSION", + "SYSCTL_VERS_0", + "SYSCTL_VERS_1", + "SYSCTL_VERS_MASK", + "SYS_ABORT2", + "SYS_ACCEPT", + "SYS_ACCEPT4", + "SYS_ACCEPT_NOCANCEL", + "SYS_ACCESS", + "SYS_ACCESS_EXTENDED", + "SYS_ACCT", + "SYS_ADD_KEY", + "SYS_ADD_PROFIL", + "SYS_ADJFREQ", + "SYS_ADJTIME", + "SYS_ADJTIMEX", + "SYS_AFS_SYSCALL", + "SYS_AIO_CANCEL", + "SYS_AIO_ERROR", + "SYS_AIO_FSYNC", + "SYS_AIO_READ", + "SYS_AIO_RETURN", + "SYS_AIO_SUSPEND", + "SYS_AIO_SUSPEND_NOCANCEL", + "SYS_AIO_WRITE", + "SYS_ALARM", + "SYS_ARCH_PRCTL", + "SYS_ARM_FADVISE64_64", + "SYS_ARM_SYNC_FILE_RANGE", + "SYS_ATGETMSG", + "SYS_ATPGETREQ", + "SYS_ATPGETRSP", + "SYS_ATPSNDREQ", + "SYS_ATPSNDRSP", + "SYS_ATPUTMSG", + "SYS_ATSOCKET", + "SYS_AUDIT", + "SYS_AUDITCTL", + "SYS_AUDITON", + "SYS_AUDIT_SESSION_JOIN", + "SYS_AUDIT_SESSION_PORT", + "SYS_AUDIT_SESSION_SELF", + "SYS_BDFLUSH", + "SYS_BIND", + "SYS_BINDAT", + "SYS_BREAK", + "SYS_BRK", + "SYS_BSDTHREAD_CREATE", + "SYS_BSDTHREAD_REGISTER", + "SYS_BSDTHREAD_TERMINATE", + "SYS_CAPGET", + "SYS_CAPSET", + "SYS_CAP_ENTER", + "SYS_CAP_FCNTLS_GET", + "SYS_CAP_FCNTLS_LIMIT", + "SYS_CAP_GETMODE", + "SYS_CAP_GETRIGHTS", + "SYS_CAP_IOCTLS_GET", + "SYS_CAP_IOCTLS_LIMIT", + "SYS_CAP_NEW", + "SYS_CAP_RIGHTS_GET", + "SYS_CAP_RIGHTS_LIMIT", + "SYS_CHDIR", + "SYS_CHFLAGS", + "SYS_CHFLAGSAT", + "SYS_CHMOD", + "SYS_CHMOD_EXTENDED", + "SYS_CHOWN", + "SYS_CHOWN32", + "SYS_CHROOT", + "SYS_CHUD", + "SYS_CLOCK_ADJTIME", + "SYS_CLOCK_GETCPUCLOCKID2", + "SYS_CLOCK_GETRES", + "SYS_CLOCK_GETTIME", + "SYS_CLOCK_NANOSLEEP", + "SYS_CLOCK_SETTIME", + "SYS_CLONE", + "SYS_CLOSE", + "SYS_CLOSEFROM", + "SYS_CLOSE_NOCANCEL", + "SYS_CONNECT", + "SYS_CONNECTAT", + "SYS_CONNECT_NOCANCEL", + "SYS_COPYFILE", + "SYS_CPUSET", + "SYS_CPUSET_GETAFFINITY", + "SYS_CPUSET_GETID", + "SYS_CPUSET_SETAFFINITY", + "SYS_CPUSET_SETID", + "SYS_CREAT", + "SYS_CREATE_MODULE", + "SYS_CSOPS", + "SYS_DELETE", + "SYS_DELETE_MODULE", + "SYS_DUP", + "SYS_DUP2", + "SYS_DUP3", + "SYS_EACCESS", + "SYS_EPOLL_CREATE", + "SYS_EPOLL_CREATE1", + "SYS_EPOLL_CTL", + "SYS_EPOLL_CTL_OLD", + "SYS_EPOLL_PWAIT", + "SYS_EPOLL_WAIT", + "SYS_EPOLL_WAIT_OLD", + "SYS_EVENTFD", + "SYS_EVENTFD2", + "SYS_EXCHANGEDATA", + "SYS_EXECVE", + "SYS_EXIT", + "SYS_EXIT_GROUP", + "SYS_EXTATTRCTL", + "SYS_EXTATTR_DELETE_FD", + "SYS_EXTATTR_DELETE_FILE", + "SYS_EXTATTR_DELETE_LINK", + "SYS_EXTATTR_GET_FD", + "SYS_EXTATTR_GET_FILE", + "SYS_EXTATTR_GET_LINK", + "SYS_EXTATTR_LIST_FD", + "SYS_EXTATTR_LIST_FILE", + "SYS_EXTATTR_LIST_LINK", + "SYS_EXTATTR_SET_FD", + "SYS_EXTATTR_SET_FILE", + "SYS_EXTATTR_SET_LINK", + "SYS_FACCESSAT", + "SYS_FADVISE64", + "SYS_FADVISE64_64", + "SYS_FALLOCATE", + "SYS_FANOTIFY_INIT", + "SYS_FANOTIFY_MARK", + "SYS_FCHDIR", + "SYS_FCHFLAGS", + "SYS_FCHMOD", + "SYS_FCHMODAT", + "SYS_FCHMOD_EXTENDED", + "SYS_FCHOWN", + "SYS_FCHOWN32", + "SYS_FCHOWNAT", + "SYS_FCHROOT", + "SYS_FCNTL", + "SYS_FCNTL64", + "SYS_FCNTL_NOCANCEL", + "SYS_FDATASYNC", + "SYS_FEXECVE", + "SYS_FFCLOCK_GETCOUNTER", + "SYS_FFCLOCK_GETESTIMATE", + "SYS_FFCLOCK_SETESTIMATE", + "SYS_FFSCTL", + "SYS_FGETATTRLIST", + "SYS_FGETXATTR", + "SYS_FHOPEN", + "SYS_FHSTAT", + "SYS_FHSTATFS", + "SYS_FILEPORT_MAKEFD", + "SYS_FILEPORT_MAKEPORT", + "SYS_FKTRACE", + "SYS_FLISTXATTR", + "SYS_FLOCK", + "SYS_FORK", + "SYS_FPATHCONF", + "SYS_FREEBSD6_FTRUNCATE", + "SYS_FREEBSD6_LSEEK", + "SYS_FREEBSD6_MMAP", + "SYS_FREEBSD6_PREAD", + "SYS_FREEBSD6_PWRITE", + "SYS_FREEBSD6_TRUNCATE", + "SYS_FREMOVEXATTR", + "SYS_FSCTL", + "SYS_FSETATTRLIST", + "SYS_FSETXATTR", + "SYS_FSGETPATH", + "SYS_FSTAT", + "SYS_FSTAT64", + "SYS_FSTAT64_EXTENDED", + "SYS_FSTATAT", + "SYS_FSTATAT64", + "SYS_FSTATFS", + "SYS_FSTATFS64", + "SYS_FSTATV", + "SYS_FSTATVFS1", + "SYS_FSTAT_EXTENDED", + "SYS_FSYNC", + "SYS_FSYNC_NOCANCEL", + "SYS_FSYNC_RANGE", + "SYS_FTIME", + "SYS_FTRUNCATE", + "SYS_FTRUNCATE64", + "SYS_FUTEX", + "SYS_FUTIMENS", + "SYS_FUTIMES", + "SYS_FUTIMESAT", + "SYS_GETATTRLIST", + "SYS_GETAUDIT", + "SYS_GETAUDIT_ADDR", + "SYS_GETAUID", + "SYS_GETCONTEXT", + "SYS_GETCPU", + "SYS_GETCWD", + "SYS_GETDENTS", + "SYS_GETDENTS64", + "SYS_GETDIRENTRIES", + "SYS_GETDIRENTRIES64", + "SYS_GETDIRENTRIESATTR", + "SYS_GETDTABLECOUNT", + "SYS_GETDTABLESIZE", + "SYS_GETEGID", + "SYS_GETEGID32", + "SYS_GETEUID", + "SYS_GETEUID32", + "SYS_GETFH", + "SYS_GETFSSTAT", + "SYS_GETFSSTAT64", + "SYS_GETGID", + "SYS_GETGID32", + "SYS_GETGROUPS", + "SYS_GETGROUPS32", + "SYS_GETHOSTUUID", + "SYS_GETITIMER", + "SYS_GETLCID", + "SYS_GETLOGIN", + "SYS_GETLOGINCLASS", + "SYS_GETPEERNAME", + "SYS_GETPGID", + "SYS_GETPGRP", + "SYS_GETPID", + "SYS_GETPMSG", + "SYS_GETPPID", + "SYS_GETPRIORITY", + "SYS_GETRESGID", + "SYS_GETRESGID32", + "SYS_GETRESUID", + "SYS_GETRESUID32", + "SYS_GETRLIMIT", + "SYS_GETRTABLE", + "SYS_GETRUSAGE", + "SYS_GETSGROUPS", + "SYS_GETSID", + "SYS_GETSOCKNAME", + "SYS_GETSOCKOPT", + "SYS_GETTHRID", + "SYS_GETTID", + "SYS_GETTIMEOFDAY", + "SYS_GETUID", + "SYS_GETUID32", + "SYS_GETVFSSTAT", + "SYS_GETWGROUPS", + "SYS_GETXATTR", + "SYS_GET_KERNEL_SYMS", + "SYS_GET_MEMPOLICY", + "SYS_GET_ROBUST_LIST", + "SYS_GET_THREAD_AREA", + "SYS_GTTY", + "SYS_IDENTITYSVC", + "SYS_IDLE", + "SYS_INITGROUPS", + "SYS_INIT_MODULE", + "SYS_INOTIFY_ADD_WATCH", + "SYS_INOTIFY_INIT", + "SYS_INOTIFY_INIT1", + "SYS_INOTIFY_RM_WATCH", + "SYS_IOCTL", + "SYS_IOPERM", + "SYS_IOPL", + "SYS_IOPOLICYSYS", + "SYS_IOPRIO_GET", + "SYS_IOPRIO_SET", + "SYS_IO_CANCEL", + "SYS_IO_DESTROY", + "SYS_IO_GETEVENTS", + "SYS_IO_SETUP", + "SYS_IO_SUBMIT", + "SYS_IPC", + "SYS_ISSETUGID", + "SYS_JAIL", + "SYS_JAIL_ATTACH", + "SYS_JAIL_GET", + "SYS_JAIL_REMOVE", + "SYS_JAIL_SET", + "SYS_KDEBUG_TRACE", + "SYS_KENV", + "SYS_KEVENT", + "SYS_KEVENT64", + "SYS_KEXEC_LOAD", + "SYS_KEYCTL", + "SYS_KILL", + "SYS_KLDFIND", + "SYS_KLDFIRSTMOD", + "SYS_KLDLOAD", + "SYS_KLDNEXT", + "SYS_KLDSTAT", + "SYS_KLDSYM", + "SYS_KLDUNLOAD", + "SYS_KLDUNLOADF", + "SYS_KQUEUE", + "SYS_KQUEUE1", + "SYS_KTIMER_CREATE", + "SYS_KTIMER_DELETE", + "SYS_KTIMER_GETOVERRUN", + "SYS_KTIMER_GETTIME", + "SYS_KTIMER_SETTIME", + "SYS_KTRACE", + "SYS_LCHFLAGS", + "SYS_LCHMOD", + "SYS_LCHOWN", + "SYS_LCHOWN32", + "SYS_LGETFH", + "SYS_LGETXATTR", + "SYS_LINK", + "SYS_LINKAT", + "SYS_LIO_LISTIO", + "SYS_LISTEN", + "SYS_LISTXATTR", + "SYS_LLISTXATTR", + "SYS_LOCK", + "SYS_LOOKUP_DCOOKIE", + "SYS_LPATHCONF", + "SYS_LREMOVEXATTR", + "SYS_LSEEK", + "SYS_LSETXATTR", + "SYS_LSTAT", + "SYS_LSTAT64", + "SYS_LSTAT64_EXTENDED", + "SYS_LSTATV", + "SYS_LSTAT_EXTENDED", + "SYS_LUTIMES", + "SYS_MAC_SYSCALL", + "SYS_MADVISE", + "SYS_MADVISE1", + "SYS_MAXSYSCALL", + "SYS_MBIND", + "SYS_MIGRATE_PAGES", + "SYS_MINCORE", + "SYS_MINHERIT", + "SYS_MKCOMPLEX", + "SYS_MKDIR", + "SYS_MKDIRAT", + "SYS_MKDIR_EXTENDED", + "SYS_MKFIFO", + "SYS_MKFIFOAT", + "SYS_MKFIFO_EXTENDED", + "SYS_MKNOD", + "SYS_MKNODAT", + "SYS_MLOCK", + "SYS_MLOCKALL", + "SYS_MMAP", + "SYS_MMAP2", + "SYS_MODCTL", + "SYS_MODFIND", + "SYS_MODFNEXT", + "SYS_MODIFY_LDT", + "SYS_MODNEXT", + "SYS_MODSTAT", + "SYS_MODWATCH", + "SYS_MOUNT", + "SYS_MOVE_PAGES", + "SYS_MPROTECT", + "SYS_MPX", + "SYS_MQUERY", + "SYS_MQ_GETSETATTR", + "SYS_MQ_NOTIFY", + "SYS_MQ_OPEN", + "SYS_MQ_TIMEDRECEIVE", + "SYS_MQ_TIMEDSEND", + "SYS_MQ_UNLINK", + "SYS_MREMAP", + "SYS_MSGCTL", + "SYS_MSGGET", + "SYS_MSGRCV", + "SYS_MSGRCV_NOCANCEL", + "SYS_MSGSND", + "SYS_MSGSND_NOCANCEL", + "SYS_MSGSYS", + "SYS_MSYNC", + "SYS_MSYNC_NOCANCEL", + "SYS_MUNLOCK", + "SYS_MUNLOCKALL", + "SYS_MUNMAP", + "SYS_NAME_TO_HANDLE_AT", + "SYS_NANOSLEEP", + "SYS_NEWFSTATAT", + "SYS_NFSCLNT", + "SYS_NFSSERVCTL", + "SYS_NFSSVC", + "SYS_NFSTAT", + "SYS_NICE", + "SYS_NLSTAT", + "SYS_NMOUNT", + "SYS_NSTAT", + "SYS_NTP_ADJTIME", + "SYS_NTP_GETTIME", + "SYS_OABI_SYSCALL_BASE", + "SYS_OBREAK", + "SYS_OLDFSTAT", + "SYS_OLDLSTAT", + "SYS_OLDOLDUNAME", + "SYS_OLDSTAT", + "SYS_OLDUNAME", + "SYS_OPEN", + "SYS_OPENAT", + "SYS_OPENBSD_POLL", + "SYS_OPEN_BY_HANDLE_AT", + "SYS_OPEN_EXTENDED", + "SYS_OPEN_NOCANCEL", + "SYS_OVADVISE", + "SYS_PACCEPT", + "SYS_PATHCONF", + "SYS_PAUSE", + "SYS_PCICONFIG_IOBASE", + "SYS_PCICONFIG_READ", + "SYS_PCICONFIG_WRITE", + "SYS_PDFORK", + "SYS_PDGETPID", + "SYS_PDKILL", + "SYS_PERF_EVENT_OPEN", + "SYS_PERSONALITY", + "SYS_PID_HIBERNATE", + "SYS_PID_RESUME", + "SYS_PID_SHUTDOWN_SOCKETS", + "SYS_PID_SUSPEND", + "SYS_PIPE", + "SYS_PIPE2", + "SYS_PIVOT_ROOT", + "SYS_PMC_CONTROL", + "SYS_PMC_GET_INFO", + "SYS_POLL", + "SYS_POLLTS", + "SYS_POLL_NOCANCEL", + "SYS_POSIX_FADVISE", + "SYS_POSIX_FALLOCATE", + "SYS_POSIX_OPENPT", + "SYS_POSIX_SPAWN", + "SYS_PPOLL", + "SYS_PRCTL", + "SYS_PREAD", + "SYS_PREAD64", + "SYS_PREADV", + "SYS_PREAD_NOCANCEL", + "SYS_PRLIMIT64", + "SYS_PROCCTL", + "SYS_PROCESS_POLICY", + "SYS_PROCESS_VM_READV", + "SYS_PROCESS_VM_WRITEV", + "SYS_PROC_INFO", + "SYS_PROF", + "SYS_PROFIL", + "SYS_PSELECT", + "SYS_PSELECT6", + "SYS_PSET_ASSIGN", + "SYS_PSET_CREATE", + "SYS_PSET_DESTROY", + "SYS_PSYNCH_CVBROAD", + "SYS_PSYNCH_CVCLRPREPOST", + "SYS_PSYNCH_CVSIGNAL", + "SYS_PSYNCH_CVWAIT", + "SYS_PSYNCH_MUTEXDROP", + "SYS_PSYNCH_MUTEXWAIT", + "SYS_PSYNCH_RW_DOWNGRADE", + "SYS_PSYNCH_RW_LONGRDLOCK", + "SYS_PSYNCH_RW_RDLOCK", + "SYS_PSYNCH_RW_UNLOCK", + "SYS_PSYNCH_RW_UNLOCK2", + "SYS_PSYNCH_RW_UPGRADE", + "SYS_PSYNCH_RW_WRLOCK", + "SYS_PSYNCH_RW_YIELDWRLOCK", + "SYS_PTRACE", + "SYS_PUTPMSG", + "SYS_PWRITE", + "SYS_PWRITE64", + "SYS_PWRITEV", + "SYS_PWRITE_NOCANCEL", + "SYS_QUERY_MODULE", + "SYS_QUOTACTL", + "SYS_RASCTL", + "SYS_RCTL_ADD_RULE", + "SYS_RCTL_GET_LIMITS", + "SYS_RCTL_GET_RACCT", + "SYS_RCTL_GET_RULES", + "SYS_RCTL_REMOVE_RULE", + "SYS_READ", + "SYS_READAHEAD", + "SYS_READDIR", + "SYS_READLINK", + "SYS_READLINKAT", + "SYS_READV", + "SYS_READV_NOCANCEL", + "SYS_READ_NOCANCEL", + "SYS_REBOOT", + "SYS_RECV", + "SYS_RECVFROM", + "SYS_RECVFROM_NOCANCEL", + "SYS_RECVMMSG", + "SYS_RECVMSG", + "SYS_RECVMSG_NOCANCEL", + "SYS_REMAP_FILE_PAGES", + "SYS_REMOVEXATTR", + "SYS_RENAME", + "SYS_RENAMEAT", + "SYS_REQUEST_KEY", + "SYS_RESTART_SYSCALL", + "SYS_REVOKE", + "SYS_RFORK", + "SYS_RMDIR", + "SYS_RTPRIO", + "SYS_RTPRIO_THREAD", + "SYS_RT_SIGACTION", + "SYS_RT_SIGPENDING", + "SYS_RT_SIGPROCMASK", + "SYS_RT_SIGQUEUEINFO", + "SYS_RT_SIGRETURN", + "SYS_RT_SIGSUSPEND", + "SYS_RT_SIGTIMEDWAIT", + "SYS_RT_TGSIGQUEUEINFO", + "SYS_SBRK", + "SYS_SCHED_GETAFFINITY", + "SYS_SCHED_GETPARAM", + "SYS_SCHED_GETSCHEDULER", + "SYS_SCHED_GET_PRIORITY_MAX", + "SYS_SCHED_GET_PRIORITY_MIN", + "SYS_SCHED_RR_GET_INTERVAL", + "SYS_SCHED_SETAFFINITY", + "SYS_SCHED_SETPARAM", + "SYS_SCHED_SETSCHEDULER", + "SYS_SCHED_YIELD", + "SYS_SCTP_GENERIC_RECVMSG", + "SYS_SCTP_GENERIC_SENDMSG", + "SYS_SCTP_GENERIC_SENDMSG_IOV", + "SYS_SCTP_PEELOFF", + "SYS_SEARCHFS", + "SYS_SECURITY", + "SYS_SELECT", + "SYS_SELECT_NOCANCEL", + "SYS_SEMCONFIG", + "SYS_SEMCTL", + "SYS_SEMGET", + "SYS_SEMOP", + "SYS_SEMSYS", + "SYS_SEMTIMEDOP", + "SYS_SEM_CLOSE", + "SYS_SEM_DESTROY", + "SYS_SEM_GETVALUE", + "SYS_SEM_INIT", + "SYS_SEM_OPEN", + "SYS_SEM_POST", + "SYS_SEM_TRYWAIT", + "SYS_SEM_UNLINK", + "SYS_SEM_WAIT", + "SYS_SEM_WAIT_NOCANCEL", + "SYS_SEND", + "SYS_SENDFILE", + "SYS_SENDFILE64", + "SYS_SENDMMSG", + "SYS_SENDMSG", + "SYS_SENDMSG_NOCANCEL", + "SYS_SENDTO", + "SYS_SENDTO_NOCANCEL", + "SYS_SETATTRLIST", + "SYS_SETAUDIT", + "SYS_SETAUDIT_ADDR", + "SYS_SETAUID", + "SYS_SETCONTEXT", + "SYS_SETDOMAINNAME", + "SYS_SETEGID", + "SYS_SETEUID", + "SYS_SETFIB", + "SYS_SETFSGID", + "SYS_SETFSGID32", + "SYS_SETFSUID", + "SYS_SETFSUID32", + "SYS_SETGID", + "SYS_SETGID32", + "SYS_SETGROUPS", + "SYS_SETGROUPS32", + "SYS_SETHOSTNAME", + "SYS_SETITIMER", + "SYS_SETLCID", + "SYS_SETLOGIN", + "SYS_SETLOGINCLASS", + "SYS_SETNS", + "SYS_SETPGID", + "SYS_SETPRIORITY", + "SYS_SETPRIVEXEC", + "SYS_SETREGID", + "SYS_SETREGID32", + "SYS_SETRESGID", + "SYS_SETRESGID32", + "SYS_SETRESUID", + "SYS_SETRESUID32", + "SYS_SETREUID", + "SYS_SETREUID32", + "SYS_SETRLIMIT", + "SYS_SETRTABLE", + "SYS_SETSGROUPS", + "SYS_SETSID", + "SYS_SETSOCKOPT", + "SYS_SETTID", + "SYS_SETTID_WITH_PID", + "SYS_SETTIMEOFDAY", + "SYS_SETUID", + "SYS_SETUID32", + "SYS_SETWGROUPS", + "SYS_SETXATTR", + "SYS_SET_MEMPOLICY", + "SYS_SET_ROBUST_LIST", + "SYS_SET_THREAD_AREA", + "SYS_SET_TID_ADDRESS", + "SYS_SGETMASK", + "SYS_SHARED_REGION_CHECK_NP", + "SYS_SHARED_REGION_MAP_AND_SLIDE_NP", + "SYS_SHMAT", + "SYS_SHMCTL", + "SYS_SHMDT", + "SYS_SHMGET", + "SYS_SHMSYS", + "SYS_SHM_OPEN", + "SYS_SHM_UNLINK", + "SYS_SHUTDOWN", + "SYS_SIGACTION", + "SYS_SIGALTSTACK", + "SYS_SIGNAL", + "SYS_SIGNALFD", + "SYS_SIGNALFD4", + "SYS_SIGPENDING", + "SYS_SIGPROCMASK", + "SYS_SIGQUEUE", + "SYS_SIGQUEUEINFO", + "SYS_SIGRETURN", + "SYS_SIGSUSPEND", + "SYS_SIGSUSPEND_NOCANCEL", + "SYS_SIGTIMEDWAIT", + "SYS_SIGWAIT", + "SYS_SIGWAITINFO", + "SYS_SOCKET", + "SYS_SOCKETCALL", + "SYS_SOCKETPAIR", + "SYS_SPLICE", + "SYS_SSETMASK", + "SYS_SSTK", + "SYS_STACK_SNAPSHOT", + "SYS_STAT", + "SYS_STAT64", + "SYS_STAT64_EXTENDED", + "SYS_STATFS", + "SYS_STATFS64", + "SYS_STATV", + "SYS_STATVFS1", + "SYS_STAT_EXTENDED", + "SYS_STIME", + "SYS_STTY", + "SYS_SWAPCONTEXT", + "SYS_SWAPCTL", + "SYS_SWAPOFF", + "SYS_SWAPON", + "SYS_SYMLINK", + "SYS_SYMLINKAT", + "SYS_SYNC", + "SYS_SYNCFS", + "SYS_SYNC_FILE_RANGE", + "SYS_SYSARCH", + "SYS_SYSCALL", + "SYS_SYSCALL_BASE", + "SYS_SYSFS", + "SYS_SYSINFO", + "SYS_SYSLOG", + "SYS_TEE", + "SYS_TGKILL", + "SYS_THREAD_SELFID", + "SYS_THR_CREATE", + "SYS_THR_EXIT", + "SYS_THR_KILL", + "SYS_THR_KILL2", + "SYS_THR_NEW", + "SYS_THR_SELF", + "SYS_THR_SET_NAME", + "SYS_THR_SUSPEND", + "SYS_THR_WAKE", + "SYS_TIME", + "SYS_TIMERFD_CREATE", + "SYS_TIMERFD_GETTIME", + "SYS_TIMERFD_SETTIME", + "SYS_TIMER_CREATE", + "SYS_TIMER_DELETE", + "SYS_TIMER_GETOVERRUN", + "SYS_TIMER_GETTIME", + "SYS_TIMER_SETTIME", + "SYS_TIMES", + "SYS_TKILL", + "SYS_TRUNCATE", + "SYS_TRUNCATE64", + "SYS_TUXCALL", + "SYS_UGETRLIMIT", + "SYS_ULIMIT", + "SYS_UMASK", + "SYS_UMASK_EXTENDED", + "SYS_UMOUNT", + "SYS_UMOUNT2", + "SYS_UNAME", + "SYS_UNDELETE", + "SYS_UNLINK", + "SYS_UNLINKAT", + "SYS_UNMOUNT", + "SYS_UNSHARE", + "SYS_USELIB", + "SYS_USTAT", + "SYS_UTIME", + "SYS_UTIMENSAT", + "SYS_UTIMES", + "SYS_UTRACE", + "SYS_UUIDGEN", + "SYS_VADVISE", + "SYS_VFORK", + "SYS_VHANGUP", + "SYS_VM86", + "SYS_VM86OLD", + "SYS_VMSPLICE", + "SYS_VM_PRESSURE_MONITOR", + "SYS_VSERVER", + "SYS_WAIT4", + "SYS_WAIT4_NOCANCEL", + "SYS_WAIT6", + "SYS_WAITEVENT", + "SYS_WAITID", + "SYS_WAITID_NOCANCEL", + "SYS_WAITPID", + "SYS_WATCHEVENT", + "SYS_WORKQ_KERNRETURN", + "SYS_WORKQ_OPEN", + "SYS_WRITE", + "SYS_WRITEV", + "SYS_WRITEV_NOCANCEL", + "SYS_WRITE_NOCANCEL", + "SYS_YIELD", + "SYS__LLSEEK", + "SYS__LWP_CONTINUE", + "SYS__LWP_CREATE", + "SYS__LWP_CTL", + "SYS__LWP_DETACH", + "SYS__LWP_EXIT", + "SYS__LWP_GETNAME", + "SYS__LWP_GETPRIVATE", + "SYS__LWP_KILL", + "SYS__LWP_PARK", + "SYS__LWP_SELF", + "SYS__LWP_SETNAME", + "SYS__LWP_SETPRIVATE", + "SYS__LWP_SUSPEND", + "SYS__LWP_UNPARK", + "SYS__LWP_UNPARK_ALL", + "SYS__LWP_WAIT", + "SYS__LWP_WAKEUP", + "SYS__NEWSELECT", + "SYS__PSET_BIND", + "SYS__SCHED_GETAFFINITY", + "SYS__SCHED_GETPARAM", + "SYS__SCHED_SETAFFINITY", + "SYS__SCHED_SETPARAM", + "SYS__SYSCTL", + "SYS__UMTX_LOCK", + "SYS__UMTX_OP", + "SYS__UMTX_UNLOCK", + "SYS___ACL_ACLCHECK_FD", + "SYS___ACL_ACLCHECK_FILE", + "SYS___ACL_ACLCHECK_LINK", + "SYS___ACL_DELETE_FD", + "SYS___ACL_DELETE_FILE", + "SYS___ACL_DELETE_LINK", + "SYS___ACL_GET_FD", + "SYS___ACL_GET_FILE", + "SYS___ACL_GET_LINK", + "SYS___ACL_SET_FD", + "SYS___ACL_SET_FILE", + "SYS___ACL_SET_LINK", + "SYS___CLONE", + "SYS___DISABLE_THREADSIGNAL", + "SYS___GETCWD", + "SYS___GETLOGIN", + "SYS___GET_TCB", + "SYS___MAC_EXECVE", + "SYS___MAC_GETFSSTAT", + "SYS___MAC_GET_FD", + "SYS___MAC_GET_FILE", + "SYS___MAC_GET_LCID", + "SYS___MAC_GET_LCTX", + "SYS___MAC_GET_LINK", + "SYS___MAC_GET_MOUNT", + "SYS___MAC_GET_PID", + "SYS___MAC_GET_PROC", + "SYS___MAC_MOUNT", + "SYS___MAC_SET_FD", + "SYS___MAC_SET_FILE", + "SYS___MAC_SET_LCTX", + "SYS___MAC_SET_LINK", + "SYS___MAC_SET_PROC", + "SYS___MAC_SYSCALL", + "SYS___OLD_SEMWAIT_SIGNAL", + "SYS___OLD_SEMWAIT_SIGNAL_NOCANCEL", + "SYS___POSIX_CHOWN", + "SYS___POSIX_FCHOWN", + "SYS___POSIX_LCHOWN", + "SYS___POSIX_RENAME", + "SYS___PTHREAD_CANCELED", + "SYS___PTHREAD_CHDIR", + "SYS___PTHREAD_FCHDIR", + "SYS___PTHREAD_KILL", + "SYS___PTHREAD_MARKCANCEL", + "SYS___PTHREAD_SIGMASK", + "SYS___QUOTACTL", + "SYS___SEMCTL", + "SYS___SEMWAIT_SIGNAL", + "SYS___SEMWAIT_SIGNAL_NOCANCEL", + "SYS___SETLOGIN", + "SYS___SETUGID", + "SYS___SET_TCB", + "SYS___SIGACTION_SIGTRAMP", + "SYS___SIGTIMEDWAIT", + "SYS___SIGWAIT", + "SYS___SIGWAIT_NOCANCEL", + "SYS___SYSCTL", + "SYS___TFORK", + "SYS___THREXIT", + "SYS___THRSIGDIVERT", + "SYS___THRSLEEP", + "SYS___THRWAKEUP", + "S_ARCH1", + "S_ARCH2", + "S_BLKSIZE", + "S_IEXEC", + "S_IFBLK", + "S_IFCHR", + "S_IFDIR", + "S_IFIFO", + "S_IFLNK", + "S_IFMT", + "S_IFREG", + "S_IFSOCK", + "S_IFWHT", + "S_IREAD", + "S_IRGRP", + "S_IROTH", + "S_IRUSR", + "S_IRWXG", + "S_IRWXO", + "S_IRWXU", + "S_ISGID", + "S_ISTXT", + "S_ISUID", + "S_ISVTX", + "S_IWGRP", + "S_IWOTH", + "S_IWRITE", + "S_IWUSR", + "S_IXGRP", + "S_IXOTH", + "S_IXUSR", + "S_LOGIN_SET", + "SecurityAttributes", + "Seek", + "Select", + "Sendfile", + "Sendmsg", + "SendmsgN", + "Sendto", + "Servent", + "SetBpf", + "SetBpfBuflen", + "SetBpfDatalink", + "SetBpfHeadercmpl", + "SetBpfImmediate", + "SetBpfInterface", + "SetBpfPromisc", + "SetBpfTimeout", + "SetCurrentDirectory", + "SetEndOfFile", + "SetEnvironmentVariable", + "SetFileAttributes", + "SetFileCompletionNotificationModes", + "SetFilePointer", + "SetFileTime", + "SetHandleInformation", + "SetKevent", + "SetLsfPromisc", + "SetNonblock", + "Setdomainname", + "Setegid", + "Setenv", + "Seteuid", + "Setfsgid", + "Setfsuid", + "Setgid", + "Setgroups", + "Sethostname", + "Setlogin", + "Setpgid", + "Setpriority", + "Setprivexec", + "Setregid", + "Setresgid", + "Setresuid", + "Setreuid", + "Setrlimit", + "Setsid", + "Setsockopt", + "SetsockoptByte", + "SetsockoptICMPv6Filter", + "SetsockoptIPMreq", + "SetsockoptIPMreqn", + "SetsockoptIPv6Mreq", + "SetsockoptInet4Addr", + "SetsockoptInt", + "SetsockoptLinger", + "SetsockoptString", + "SetsockoptTimeval", + "Settimeofday", + "Setuid", + "Setxattr", + "Shutdown", + "SidTypeAlias", + "SidTypeComputer", + "SidTypeDeletedAccount", + "SidTypeDomain", + "SidTypeGroup", + "SidTypeInvalid", + "SidTypeLabel", + "SidTypeUnknown", + "SidTypeUser", + "SidTypeWellKnownGroup", + "Signal", + "SizeofBpfHdr", + "SizeofBpfInsn", + "SizeofBpfProgram", + "SizeofBpfStat", + "SizeofBpfVersion", + "SizeofBpfZbuf", + "SizeofBpfZbufHeader", + "SizeofCmsghdr", + "SizeofICMPv6Filter", + "SizeofIPMreq", + "SizeofIPMreqn", + "SizeofIPv6MTUInfo", + "SizeofIPv6Mreq", + "SizeofIfAddrmsg", + "SizeofIfAnnounceMsghdr", + "SizeofIfData", + "SizeofIfInfomsg", + "SizeofIfMsghdr", + "SizeofIfaMsghdr", + "SizeofIfmaMsghdr", + "SizeofIfmaMsghdr2", + "SizeofInet4Pktinfo", + "SizeofInet6Pktinfo", + "SizeofInotifyEvent", + "SizeofLinger", + "SizeofMsghdr", + "SizeofNlAttr", + "SizeofNlMsgerr", + "SizeofNlMsghdr", + "SizeofRtAttr", + "SizeofRtGenmsg", + "SizeofRtMetrics", + "SizeofRtMsg", + "SizeofRtMsghdr", + "SizeofRtNexthop", + "SizeofSockFilter", + "SizeofSockFprog", + "SizeofSockaddrAny", + "SizeofSockaddrDatalink", + "SizeofSockaddrInet4", + "SizeofSockaddrInet6", + "SizeofSockaddrLinklayer", + "SizeofSockaddrNetlink", + "SizeofSockaddrUnix", + "SizeofTCPInfo", + "SizeofUcred", + "SlicePtrFromStrings", + "SockFilter", + "SockFprog", + "Sockaddr", + "SockaddrDatalink", + "SockaddrGen", + "SockaddrInet4", + "SockaddrInet6", + "SockaddrLinklayer", + "SockaddrNetlink", + "SockaddrUnix", + "Socket", + "SocketControlMessage", + "SocketDisableIPv6", + "Socketpair", + "Splice", + "StartProcess", + "StartupInfo", + "Stat", + "Stat_t", + "Statfs", + "Statfs_t", + "Stderr", + "Stdin", + "Stdout", + "StringBytePtr", + "StringByteSlice", + "StringSlicePtr", + "StringToSid", + "StringToUTF16", + "StringToUTF16Ptr", + "Symlink", + "Sync", + "SyncFileRange", + "SysProcAttr", + "SysProcIDMap", + "Syscall", + "Syscall12", + "Syscall15", + "Syscall18", + "Syscall6", + "Syscall9", + "SyscallN", + "Sysctl", + "SysctlUint32", + "Sysctlnode", + "Sysinfo", + "Sysinfo_t", + "Systemtime", + "TCGETS", + "TCIFLUSH", + "TCIOFLUSH", + "TCOFLUSH", + "TCPInfo", + "TCPKeepalive", + "TCP_CA_NAME_MAX", + "TCP_CONGCTL", + "TCP_CONGESTION", + "TCP_CONNECTIONTIMEOUT", + "TCP_CORK", + "TCP_DEFER_ACCEPT", + "TCP_INFO", + "TCP_KEEPALIVE", + "TCP_KEEPCNT", + "TCP_KEEPIDLE", + "TCP_KEEPINIT", + "TCP_KEEPINTVL", + "TCP_LINGER2", + "TCP_MAXBURST", + "TCP_MAXHLEN", + "TCP_MAXOLEN", + "TCP_MAXSEG", + "TCP_MAXWIN", + "TCP_MAX_SACK", + "TCP_MAX_WINSHIFT", + "TCP_MD5SIG", + "TCP_MD5SIG_MAXKEYLEN", + "TCP_MINMSS", + "TCP_MINMSSOVERLOAD", + "TCP_MSS", + "TCP_NODELAY", + "TCP_NOOPT", + "TCP_NOPUSH", + "TCP_NSTATES", + "TCP_QUICKACK", + "TCP_RXT_CONNDROPTIME", + "TCP_RXT_FINDROP", + "TCP_SACK_ENABLE", + "TCP_SYNCNT", + "TCP_VENDOR", + "TCP_WINDOW_CLAMP", + "TCSAFLUSH", + "TCSETS", + "TF_DISCONNECT", + "TF_REUSE_SOCKET", + "TF_USE_DEFAULT_WORKER", + "TF_USE_KERNEL_APC", + "TF_USE_SYSTEM_THREAD", + "TF_WRITE_BEHIND", + "TH32CS_INHERIT", + "TH32CS_SNAPALL", + "TH32CS_SNAPHEAPLIST", + "TH32CS_SNAPMODULE", + "TH32CS_SNAPMODULE32", + "TH32CS_SNAPPROCESS", + "TH32CS_SNAPTHREAD", + "TIME_ZONE_ID_DAYLIGHT", + "TIME_ZONE_ID_STANDARD", + "TIME_ZONE_ID_UNKNOWN", + "TIOCCBRK", + "TIOCCDTR", + "TIOCCONS", + "TIOCDCDTIMESTAMP", + "TIOCDRAIN", + "TIOCDSIMICROCODE", + "TIOCEXCL", + "TIOCEXT", + "TIOCFLAG_CDTRCTS", + "TIOCFLAG_CLOCAL", + "TIOCFLAG_CRTSCTS", + "TIOCFLAG_MDMBUF", + "TIOCFLAG_PPS", + "TIOCFLAG_SOFTCAR", + "TIOCFLUSH", + "TIOCGDEV", + "TIOCGDRAINWAIT", + "TIOCGETA", + "TIOCGETD", + "TIOCGFLAGS", + "TIOCGICOUNT", + "TIOCGLCKTRMIOS", + "TIOCGLINED", + "TIOCGPGRP", + "TIOCGPTN", + "TIOCGQSIZE", + "TIOCGRANTPT", + "TIOCGRS485", + "TIOCGSERIAL", + "TIOCGSID", + "TIOCGSIZE", + "TIOCGSOFTCAR", + "TIOCGTSTAMP", + "TIOCGWINSZ", + "TIOCINQ", + "TIOCIXOFF", + "TIOCIXON", + "TIOCLINUX", + "TIOCMBIC", + "TIOCMBIS", + "TIOCMGDTRWAIT", + "TIOCMGET", + "TIOCMIWAIT", + "TIOCMODG", + "TIOCMODS", + "TIOCMSDTRWAIT", + "TIOCMSET", + "TIOCM_CAR", + "TIOCM_CD", + "TIOCM_CTS", + "TIOCM_DCD", + "TIOCM_DSR", + "TIOCM_DTR", + "TIOCM_LE", + "TIOCM_RI", + "TIOCM_RNG", + "TIOCM_RTS", + "TIOCM_SR", + "TIOCM_ST", + "TIOCNOTTY", + "TIOCNXCL", + "TIOCOUTQ", + "TIOCPKT", + "TIOCPKT_DATA", + "TIOCPKT_DOSTOP", + "TIOCPKT_FLUSHREAD", + "TIOCPKT_FLUSHWRITE", + "TIOCPKT_IOCTL", + "TIOCPKT_NOSTOP", + "TIOCPKT_START", + "TIOCPKT_STOP", + "TIOCPTMASTER", + "TIOCPTMGET", + "TIOCPTSNAME", + "TIOCPTYGNAME", + "TIOCPTYGRANT", + "TIOCPTYUNLK", + "TIOCRCVFRAME", + "TIOCREMOTE", + "TIOCSBRK", + "TIOCSCONS", + "TIOCSCTTY", + "TIOCSDRAINWAIT", + "TIOCSDTR", + "TIOCSERCONFIG", + "TIOCSERGETLSR", + "TIOCSERGETMULTI", + "TIOCSERGSTRUCT", + "TIOCSERGWILD", + "TIOCSERSETMULTI", + "TIOCSERSWILD", + "TIOCSER_TEMT", + "TIOCSETA", + "TIOCSETAF", + "TIOCSETAW", + "TIOCSETD", + "TIOCSFLAGS", + "TIOCSIG", + "TIOCSLCKTRMIOS", + "TIOCSLINED", + "TIOCSPGRP", + "TIOCSPTLCK", + "TIOCSQSIZE", + "TIOCSRS485", + "TIOCSSERIAL", + "TIOCSSIZE", + "TIOCSSOFTCAR", + "TIOCSTART", + "TIOCSTAT", + "TIOCSTI", + "TIOCSTOP", + "TIOCSTSTAMP", + "TIOCSWINSZ", + "TIOCTIMESTAMP", + "TIOCUCNTL", + "TIOCVHANGUP", + "TIOCXMTFRAME", + "TOKEN_ADJUST_DEFAULT", + "TOKEN_ADJUST_GROUPS", + "TOKEN_ADJUST_PRIVILEGES", + "TOKEN_ADJUST_SESSIONID", + "TOKEN_ALL_ACCESS", + "TOKEN_ASSIGN_PRIMARY", + "TOKEN_DUPLICATE", + "TOKEN_EXECUTE", + "TOKEN_IMPERSONATE", + "TOKEN_QUERY", + "TOKEN_QUERY_SOURCE", + "TOKEN_READ", + "TOKEN_WRITE", + "TOSTOP", + "TRUNCATE_EXISTING", + "TUNATTACHFILTER", + "TUNDETACHFILTER", + "TUNGETFEATURES", + "TUNGETIFF", + "TUNGETSNDBUF", + "TUNGETVNETHDRSZ", + "TUNSETDEBUG", + "TUNSETGROUP", + "TUNSETIFF", + "TUNSETLINK", + "TUNSETNOCSUM", + "TUNSETOFFLOAD", + "TUNSETOWNER", + "TUNSETPERSIST", + "TUNSETSNDBUF", + "TUNSETTXFILTER", + "TUNSETVNETHDRSZ", + "Tee", + "TerminateProcess", + "Termios", + "Tgkill", + "Time", + "Time_t", + "Times", + "Timespec", + "TimespecToNsec", + "Timeval", + "Timeval32", + "TimevalToNsec", + "Timex", + "Timezoneinformation", + "Tms", + "Token", + "TokenAccessInformation", + "TokenAuditPolicy", + "TokenDefaultDacl", + "TokenElevation", + "TokenElevationType", + "TokenGroups", + "TokenGroupsAndPrivileges", + "TokenHasRestrictions", + "TokenImpersonationLevel", + "TokenIntegrityLevel", + "TokenLinkedToken", + "TokenLogonSid", + "TokenMandatoryPolicy", + "TokenOrigin", + "TokenOwner", + "TokenPrimaryGroup", + "TokenPrivileges", + "TokenRestrictedSids", + "TokenSandBoxInert", + "TokenSessionId", + "TokenSessionReference", + "TokenSource", + "TokenStatistics", + "TokenType", + "TokenUIAccess", + "TokenUser", + "TokenVirtualizationAllowed", + "TokenVirtualizationEnabled", + "Tokenprimarygroup", + "Tokenuser", + "TranslateAccountName", + "TranslateName", + "TransmitFile", + "TransmitFileBuffers", + "Truncate", + "UNIX_PATH_MAX", + "USAGE_MATCH_TYPE_AND", + "USAGE_MATCH_TYPE_OR", + "UTF16FromString", + "UTF16PtrFromString", + "UTF16ToString", + "Ucred", + "Umask", + "Uname", + "Undelete", + "UnixCredentials", + "UnixRights", + "Unlink", + "Unlinkat", + "UnmapViewOfFile", + "Unmount", + "Unsetenv", + "Unshare", + "UserInfo10", + "Ustat", + "Ustat_t", + "Utimbuf", + "Utime", + "Utimes", + "UtimesNano", + "Utsname", + "VDISCARD", + "VDSUSP", + "VEOF", + "VEOL", + "VEOL2", + "VERASE", + "VERASE2", + "VINTR", + "VKILL", + "VLNEXT", + "VMIN", + "VQUIT", + "VREPRINT", + "VSTART", + "VSTATUS", + "VSTOP", + "VSUSP", + "VSWTC", + "VT0", + "VT1", + "VTDLY", + "VTIME", + "VWERASE", + "VirtualLock", + "VirtualUnlock", + "WAIT_ABANDONED", + "WAIT_FAILED", + "WAIT_OBJECT_0", + "WAIT_TIMEOUT", + "WALL", + "WALLSIG", + "WALTSIG", + "WCLONE", + "WCONTINUED", + "WCOREFLAG", + "WEXITED", + "WLINUXCLONE", + "WNOHANG", + "WNOTHREAD", + "WNOWAIT", + "WNOZOMBIE", + "WOPTSCHECKED", + "WORDSIZE", + "WSABuf", + "WSACleanup", + "WSADESCRIPTION_LEN", + "WSAData", + "WSAEACCES", + "WSAECONNABORTED", + "WSAECONNRESET", + "WSAEnumProtocols", + "WSAID_CONNECTEX", + "WSAIoctl", + "WSAPROTOCOL_LEN", + "WSAProtocolChain", + "WSAProtocolInfo", + "WSARecv", + "WSARecvFrom", + "WSASYS_STATUS_LEN", + "WSASend", + "WSASendTo", + "WSASendto", + "WSAStartup", + "WSTOPPED", + "WTRAPPED", + "WUNTRACED", + "Wait4", + "WaitForSingleObject", + "WaitStatus", + "Win32FileAttributeData", + "Win32finddata", + "Write", + "WriteConsole", + "WriteFile", + "X509_ASN_ENCODING", + "XCASE", + "XP1_CONNECTIONLESS", + "XP1_CONNECT_DATA", + "XP1_DISCONNECT_DATA", + "XP1_EXPEDITED_DATA", + "XP1_GRACEFUL_CLOSE", + "XP1_GUARANTEED_DELIVERY", + "XP1_GUARANTEED_ORDER", + "XP1_IFS_HANDLES", + "XP1_MESSAGE_ORIENTED", + "XP1_MULTIPOINT_CONTROL_PLANE", + "XP1_MULTIPOINT_DATA_PLANE", + "XP1_PARTIAL_MESSAGE", + "XP1_PSEUDO_STREAM", + "XP1_QOS_SUPPORTED", + "XP1_SAN_SUPPORT_SDP", + "XP1_SUPPORT_BROADCAST", + "XP1_SUPPORT_MULTIPOINT", + "XP1_UNI_RECV", + "XP1_UNI_SEND", + }, + "syscall/js": { + "CopyBytesToGo", + "CopyBytesToJS", + "Error", + "Func", + "FuncOf", + "Global", + "Null", + "Type", + "TypeBoolean", + "TypeFunction", + "TypeNull", + "TypeNumber", + "TypeObject", + "TypeString", + "TypeSymbol", + "TypeUndefined", + "Undefined", + "Value", + "ValueError", + "ValueOf", + }, + "testing": { + "AllocsPerRun", + "B", + "Benchmark", + "BenchmarkResult", + "Cover", + "CoverBlock", + "CoverMode", + "Coverage", + "F", + "Init", + "InternalBenchmark", + "InternalExample", + "InternalFuzzTarget", + "InternalTest", + "M", + "Main", + "MainStart", + "PB", + "RegisterCover", + "RunBenchmarks", + "RunExamples", + "RunTests", + "Short", + "T", + "TB", + "Verbose", + }, + "testing/fstest": { + "MapFS", + "MapFile", + "TestFS", + }, + "testing/iotest": { + "DataErrReader", + "ErrReader", + "ErrTimeout", + "HalfReader", + "NewReadLogger", + "NewWriteLogger", + "OneByteReader", + "TestReader", + "TimeoutReader", + "TruncateWriter", + }, + "testing/quick": { + "Check", + "CheckEqual", + "CheckEqualError", + "CheckError", + "Config", + "Generator", + "SetupError", + "Value", + }, + "text/scanner": { + "Char", + "Comment", + "EOF", + "Float", + "GoTokens", + "GoWhitespace", + "Ident", + "Int", + "Position", + "RawString", + "ScanChars", + "ScanComments", + "ScanFloats", + "ScanIdents", + "ScanInts", + "ScanRawStrings", + "ScanStrings", + "Scanner", + "SkipComments", + "String", + "TokenString", + }, + "text/tabwriter": { + "AlignRight", + "Debug", + "DiscardEmptyColumns", + "Escape", + "FilterHTML", + "NewWriter", + "StripEscape", + "TabIndent", + "Writer", + }, + "text/template": { + "ExecError", + "FuncMap", + "HTMLEscape", + "HTMLEscapeString", + "HTMLEscaper", + "IsTrue", + "JSEscape", + "JSEscapeString", + "JSEscaper", + "Must", + "New", + "ParseFS", + "ParseFiles", + "ParseGlob", + "Template", + "URLQueryEscaper", + }, + "text/template/parse": { + "ActionNode", + "BoolNode", + "BranchNode", + "BreakNode", + "ChainNode", + "CommandNode", + "CommentNode", + "ContinueNode", + "DotNode", + "FieldNode", + "IdentifierNode", + "IfNode", + "IsEmptyTree", + "ListNode", + "Mode", + "New", + "NewIdentifier", + "NilNode", + "Node", + "NodeAction", + "NodeBool", + "NodeBreak", + "NodeChain", + "NodeCommand", + "NodeComment", + "NodeContinue", + "NodeDot", + "NodeField", + "NodeIdentifier", + "NodeIf", + "NodeList", + "NodeNil", + "NodeNumber", + "NodePipe", + "NodeRange", + "NodeString", + "NodeTemplate", + "NodeText", + "NodeType", + "NodeVariable", + "NodeWith", + "NumberNode", + "Parse", + "ParseComments", + "PipeNode", + "Pos", + "RangeNode", + "SkipFuncCheck", + "StringNode", + "TemplateNode", + "TextNode", + "Tree", + "VariableNode", + "WithNode", + }, + "time": { + "ANSIC", + "After", + "AfterFunc", + "April", + "August", + "Date", + "December", + "Duration", + "February", + "FixedZone", + "Friday", + "Hour", + "January", + "July", + "June", + "Kitchen", + "Layout", + "LoadLocation", + "LoadLocationFromTZData", + "Local", + "Location", + "March", + "May", + "Microsecond", + "Millisecond", + "Minute", + "Monday", + "Month", + "Nanosecond", + "NewTicker", + "NewTimer", + "November", + "Now", + "October", + "Parse", + "ParseDuration", + "ParseError", + "ParseInLocation", + "RFC1123", + "RFC1123Z", + "RFC3339", + "RFC3339Nano", + "RFC822", + "RFC822Z", + "RFC850", + "RubyDate", + "Saturday", + "Second", + "September", + "Since", + "Sleep", + "Stamp", + "StampMicro", + "StampMilli", + "StampNano", + "Sunday", + "Thursday", + "Tick", + "Ticker", + "Time", + "Timer", + "Tuesday", + "UTC", + "Unix", + "UnixDate", + "UnixMicro", + "UnixMilli", + "Until", + "Wednesday", + "Weekday", + }, + "unicode": { + "ASCII_Hex_Digit", + "Adlam", + "Ahom", + "Anatolian_Hieroglyphs", + "Arabic", + "Armenian", + "Avestan", + "AzeriCase", + "Balinese", + "Bamum", + "Bassa_Vah", + "Batak", + "Bengali", + "Bhaiksuki", + "Bidi_Control", + "Bopomofo", + "Brahmi", + "Braille", + "Buginese", + "Buhid", + "C", + "Canadian_Aboriginal", + "Carian", + "CaseRange", + "CaseRanges", + "Categories", + "Caucasian_Albanian", + "Cc", + "Cf", + "Chakma", + "Cham", + "Cherokee", + "Chorasmian", + "Co", + "Common", + "Coptic", + "Cs", + "Cuneiform", + "Cypriot", + "Cyrillic", + "Dash", + "Deprecated", + "Deseret", + "Devanagari", + "Diacritic", + "Digit", + "Dives_Akuru", + "Dogra", + "Duployan", + "Egyptian_Hieroglyphs", + "Elbasan", + "Elymaic", + "Ethiopic", + "Extender", + "FoldCategory", + "FoldScript", + "Georgian", + "Glagolitic", + "Gothic", + "Grantha", + "GraphicRanges", + "Greek", + "Gujarati", + "Gunjala_Gondi", + "Gurmukhi", + "Han", + "Hangul", + "Hanifi_Rohingya", + "Hanunoo", + "Hatran", + "Hebrew", + "Hex_Digit", + "Hiragana", + "Hyphen", + "IDS_Binary_Operator", + "IDS_Trinary_Operator", + "Ideographic", + "Imperial_Aramaic", + "In", + "Inherited", + "Inscriptional_Pahlavi", + "Inscriptional_Parthian", + "Is", + "IsControl", + "IsDigit", + "IsGraphic", + "IsLetter", + "IsLower", + "IsMark", + "IsNumber", + "IsOneOf", + "IsPrint", + "IsPunct", + "IsSpace", + "IsSymbol", + "IsTitle", + "IsUpper", + "Javanese", + "Join_Control", + "Kaithi", + "Kannada", + "Katakana", + "Kayah_Li", + "Kharoshthi", + "Khitan_Small_Script", + "Khmer", + "Khojki", + "Khudawadi", + "L", + "Lao", + "Latin", + "Lepcha", + "Letter", + "Limbu", + "Linear_A", + "Linear_B", + "Lisu", + "Ll", + "Lm", + "Lo", + "Logical_Order_Exception", + "Lower", + "LowerCase", + "Lt", + "Lu", + "Lycian", + "Lydian", + "M", + "Mahajani", + "Makasar", + "Malayalam", + "Mandaic", + "Manichaean", + "Marchen", + "Mark", + "Masaram_Gondi", + "MaxASCII", + "MaxCase", + "MaxLatin1", + "MaxRune", + "Mc", + "Me", + "Medefaidrin", + "Meetei_Mayek", + "Mende_Kikakui", + "Meroitic_Cursive", + "Meroitic_Hieroglyphs", + "Miao", + "Mn", + "Modi", + "Mongolian", + "Mro", + "Multani", + "Myanmar", + "N", + "Nabataean", + "Nandinagari", + "Nd", + "New_Tai_Lue", + "Newa", + "Nko", + "Nl", + "No", + "Noncharacter_Code_Point", + "Number", + "Nushu", + "Nyiakeng_Puachue_Hmong", + "Ogham", + "Ol_Chiki", + "Old_Hungarian", + "Old_Italic", + "Old_North_Arabian", + "Old_Permic", + "Old_Persian", + "Old_Sogdian", + "Old_South_Arabian", + "Old_Turkic", + "Oriya", + "Osage", + "Osmanya", + "Other", + "Other_Alphabetic", + "Other_Default_Ignorable_Code_Point", + "Other_Grapheme_Extend", + "Other_ID_Continue", + "Other_ID_Start", + "Other_Lowercase", + "Other_Math", + "Other_Uppercase", + "P", + "Pahawh_Hmong", + "Palmyrene", + "Pattern_Syntax", + "Pattern_White_Space", + "Pau_Cin_Hau", + "Pc", + "Pd", + "Pe", + "Pf", + "Phags_Pa", + "Phoenician", + "Pi", + "Po", + "Prepended_Concatenation_Mark", + "PrintRanges", + "Properties", + "Ps", + "Psalter_Pahlavi", + "Punct", + "Quotation_Mark", + "Radical", + "Range16", + "Range32", + "RangeTable", + "Regional_Indicator", + "Rejang", + "ReplacementChar", + "Runic", + "S", + "STerm", + "Samaritan", + "Saurashtra", + "Sc", + "Scripts", + "Sentence_Terminal", + "Sharada", + "Shavian", + "Siddham", + "SignWriting", + "SimpleFold", + "Sinhala", + "Sk", + "Sm", + "So", + "Soft_Dotted", + "Sogdian", + "Sora_Sompeng", + "Soyombo", + "Space", + "SpecialCase", + "Sundanese", + "Syloti_Nagri", + "Symbol", + "Syriac", + "Tagalog", + "Tagbanwa", + "Tai_Le", + "Tai_Tham", + "Tai_Viet", + "Takri", + "Tamil", + "Tangut", + "Telugu", + "Terminal_Punctuation", + "Thaana", + "Thai", + "Tibetan", + "Tifinagh", + "Tirhuta", + "Title", + "TitleCase", + "To", + "ToLower", + "ToTitle", + "ToUpper", + "TurkishCase", + "Ugaritic", + "Unified_Ideograph", + "Upper", + "UpperCase", + "UpperLower", + "Vai", + "Variation_Selector", + "Version", + "Wancho", + "Warang_Citi", + "White_Space", + "Yezidi", + "Yi", + "Z", + "Zanabazar_Square", + "Zl", + "Zp", + "Zs", + }, + "unicode/utf16": { + "Decode", + "DecodeRune", + "Encode", + "EncodeRune", + "IsSurrogate", + }, + "unicode/utf8": { + "AppendRune", + "DecodeLastRune", + "DecodeLastRuneInString", + "DecodeRune", + "DecodeRuneInString", + "EncodeRune", + "FullRune", + "FullRuneInString", + "MaxRune", + "RuneCount", + "RuneCountInString", + "RuneError", + "RuneLen", + "RuneSelf", + "RuneStart", + "UTFMax", + "Valid", + "ValidRune", + "ValidString", + }, + "unsafe": { + "Alignof", + "ArbitraryType", + "Offsetof", + "Pointer", + "Sizeof", + }, +} diff --git a/gopls/golang_org_x_tools/jsonrpc2/conn.go b/gopls/golang_org_x_tools/jsonrpc2/conn.go new file mode 100644 index 0000000..fc8ae59 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2/conn.go @@ -0,0 +1,262 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "context" + "encoding/json" + "fmt" + "sync" + "sync/atomic" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/tag" +) + +// Conn is the common interface to jsonrpc clients and servers. +// Conn is bidirectional; it does not have a designated server or client end. +// It manages the jsonrpc2 protocol, connecting responses back to their calls. +type Conn interface { + // Call invokes the target method and waits for a response. + // The params will be marshaled to JSON before sending over the wire, and will + // be handed to the method invoked. + // The response will be unmarshaled from JSON into the result. + // The id returned will be unique from this connection, and can be used for + // logging or tracking. + Call(ctx context.Context, method string, params, result interface{}) (ID, error) + + // Notify invokes the target method but does not wait for a response. + // The params will be marshaled to JSON before sending over the wire, and will + // be handed to the method invoked. + Notify(ctx context.Context, method string, params interface{}) error + + // Go starts a goroutine to handle the connection. + // It must be called exactly once for each Conn. + // It returns immediately. + // You must block on Done() to wait for the connection to shut down. + // This is a temporary measure, this should be started automatically in the + // future. + Go(ctx context.Context, handler Handler) + + // Close closes the connection and it's underlying stream. + // It does not wait for the close to complete, use the Done() channel for + // that. + Close() error + + // Done returns a channel that will be closed when the processing goroutine + // has terminated, which will happen if Close() is called or an underlying + // stream is closed. + Done() <-chan struct{} + + // Err returns an error if there was one from within the processing goroutine. + // If err returns non nil, the connection will be already closed or closing. + Err() error +} + +type conn struct { + seq int64 // must only be accessed using atomic operations + writeMu sync.Mutex // protects writes to the stream + stream Stream + pendingMu sync.Mutex // protects the pending map + pending map[ID]chan *Response + + done chan struct{} + err atomic.Value +} + +// NewConn creates a new connection object around the supplied stream. +func NewConn(s Stream) Conn { + conn := &conn{ + stream: s, + pending: make(map[ID]chan *Response), + done: make(chan struct{}), + } + return conn +} + +func (c *conn) Notify(ctx context.Context, method string, params interface{}) (err error) { + notify, err := NewNotification(method, params) + if err != nil { + return fmt.Errorf("marshaling notify parameters: %v", err) + } + ctx, done := event.Start(ctx, method, + tag.Method.Of(method), + tag.RPCDirection.Of(tag.Outbound), + ) + defer func() { + recordStatus(ctx, err) + done() + }() + + event.Metric(ctx, tag.Started.Of(1)) + n, err := c.write(ctx, notify) + event.Metric(ctx, tag.SentBytes.Of(n)) + return err +} + +func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (_ ID, err error) { + // generate a new request identifier + id := ID{number: atomic.AddInt64(&c.seq, 1)} + call, err := NewCall(id, method, params) + if err != nil { + return id, fmt.Errorf("marshaling call parameters: %v", err) + } + ctx, done := event.Start(ctx, method, + tag.Method.Of(method), + tag.RPCDirection.Of(tag.Outbound), + tag.RPCID.Of(fmt.Sprintf("%q", id)), + ) + defer func() { + recordStatus(ctx, err) + done() + }() + event.Metric(ctx, tag.Started.Of(1)) + // We have to add ourselves to the pending map before we send, otherwise we + // are racing the response. Also add a buffer to rchan, so that if we get a + // wire response between the time this call is cancelled and id is deleted + // from c.pending, the send to rchan will not block. + rchan := make(chan *Response, 1) + c.pendingMu.Lock() + c.pending[id] = rchan + c.pendingMu.Unlock() + defer func() { + c.pendingMu.Lock() + delete(c.pending, id) + c.pendingMu.Unlock() + }() + // now we are ready to send + n, err := c.write(ctx, call) + event.Metric(ctx, tag.SentBytes.Of(n)) + if err != nil { + // sending failed, we will never get a response, so don't leave it pending + return id, err + } + // now wait for the response + select { + case response := <-rchan: + // is it an error response? + if response.err != nil { + return id, response.err + } + if result == nil || len(response.result) == 0 { + return id, nil + } + if err := json.Unmarshal(response.result, result); err != nil { + return id, fmt.Errorf("unmarshaling result: %v", err) + } + return id, nil + case <-ctx.Done(): + return id, ctx.Err() + } +} + +func (c *conn) replier(req Request, spanDone func()) Replier { + return func(ctx context.Context, result interface{}, err error) error { + defer func() { + recordStatus(ctx, err) + spanDone() + }() + call, ok := req.(*Call) + if !ok { + // request was a notify, no need to respond + return nil + } + response, err := NewResponse(call.id, result, err) + if err != nil { + return err + } + n, err := c.write(ctx, response) + event.Metric(ctx, tag.SentBytes.Of(n)) + if err != nil { + // TODO(iancottrell): if a stream write fails, we really need to shut down + // the whole stream + return err + } + return nil + } +} + +func (c *conn) write(ctx context.Context, msg Message) (int64, error) { + c.writeMu.Lock() + defer c.writeMu.Unlock() + return c.stream.Write(ctx, msg) +} + +func (c *conn) Go(ctx context.Context, handler Handler) { + go c.run(ctx, handler) +} + +func (c *conn) run(ctx context.Context, handler Handler) { + defer close(c.done) + for { + // get the next message + msg, n, err := c.stream.Read(ctx) + if err != nil { + // The stream failed, we cannot continue. + c.fail(err) + return + } + switch msg := msg.(type) { + case Request: + labels := []label.Label{ + tag.Method.Of(msg.Method()), + tag.RPCDirection.Of(tag.Inbound), + {}, // reserved for ID if present + } + if call, ok := msg.(*Call); ok { + labels[len(labels)-1] = tag.RPCID.Of(fmt.Sprintf("%q", call.ID())) + } else { + labels = labels[:len(labels)-1] + } + reqCtx, spanDone := event.Start(ctx, msg.Method(), labels...) + event.Metric(reqCtx, + tag.Started.Of(1), + tag.ReceivedBytes.Of(n)) + if err := handler(reqCtx, c.replier(msg, spanDone), msg); err != nil { + // delivery failed, not much we can do + event.Error(reqCtx, "jsonrpc2 message delivery failed", err) + } + case *Response: + // If method is not set, this should be a response, in which case we must + // have an id to send the response back to the caller. + c.pendingMu.Lock() + rchan, ok := c.pending[msg.id] + c.pendingMu.Unlock() + if ok { + rchan <- msg + } + } + } +} + +func (c *conn) Close() error { + return c.stream.Close() +} + +func (c *conn) Done() <-chan struct{} { + return c.done +} + +func (c *conn) Err() error { + if err := c.err.Load(); err != nil { + return err.(error) + } + return nil +} + +// fail sets a failure condition on the stream and closes it. +func (c *conn) fail(err error) { + c.err.Store(err) + c.stream.Close() +} + +func recordStatus(ctx context.Context, err error) { + if err != nil { + event.Label(ctx, tag.StatusCode.Of("ERROR")) + } else { + event.Label(ctx, tag.StatusCode.Of("OK")) + } +} diff --git a/gopls/golang_org_x_tools/jsonrpc2/handler.go b/gopls/golang_org_x_tools/jsonrpc2/handler.go new file mode 100644 index 0000000..f685943 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2/handler.go @@ -0,0 +1,109 @@ +// 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 jsonrpc2 + +import ( + "context" + "fmt" + "sync" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +// Handler is invoked to handle incoming requests. +// The Replier sends a reply to the request and must be called exactly once. +type Handler func(ctx context.Context, reply Replier, req Request) error + +// Replier is passed to handlers to allow them to reply to the request. +// If err is set then result will be ignored. +type Replier func(ctx context.Context, result interface{}, err error) error + +// MethodNotFound is a Handler that replies to all call requests with the +// standard method not found response. +// This should normally be the final handler in a chain. +func MethodNotFound(ctx context.Context, reply Replier, req Request) error { + return reply(ctx, nil, fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method())) +} + +// MustReplyHandler creates a Handler that panics if the wrapped handler does +// not call Reply for every request that it is passed. +func MustReplyHandler(handler Handler) Handler { + return func(ctx context.Context, reply Replier, req Request) error { + called := false + err := handler(ctx, func(ctx context.Context, result interface{}, err error) error { + if called { + panic(fmt.Errorf("request %q replied to more than once", req.Method())) + } + called = true + return reply(ctx, result, err) + }, req) + if !called { + panic(fmt.Errorf("request %q was never replied to", req.Method())) + } + return err + } +} + +// CancelHandler returns a handler that supports cancellation, and a function +// that can be used to trigger canceling in progress requests. +func CancelHandler(handler Handler) (Handler, func(id ID)) { + var mu sync.Mutex + handling := make(map[ID]context.CancelFunc) + wrapped := func(ctx context.Context, reply Replier, req Request) error { + if call, ok := req.(*Call); ok { + cancelCtx, cancel := context.WithCancel(ctx) + ctx = cancelCtx + mu.Lock() + handling[call.ID()] = cancel + mu.Unlock() + innerReply := reply + reply = func(ctx context.Context, result interface{}, err error) error { + mu.Lock() + delete(handling, call.ID()) + mu.Unlock() + return innerReply(ctx, result, err) + } + } + return handler(ctx, reply, req) + } + return wrapped, func(id ID) { + mu.Lock() + cancel, found := handling[id] + mu.Unlock() + if found { + cancel() + } + } +} + +// AsyncHandler returns a handler that processes each request goes in its own +// goroutine. +// The handler returns immediately, without the request being processed. +// Each request then waits for the previous request to finish before it starts. +// This allows the stream to unblock at the cost of unbounded goroutines +// all stalled on the previous one. +func AsyncHandler(handler Handler) Handler { + nextRequest := make(chan struct{}) + close(nextRequest) + return func(ctx context.Context, reply Replier, req Request) error { + waitForPrevious := nextRequest + nextRequest = make(chan struct{}) + unlockNext := nextRequest + innerReply := reply + reply = func(ctx context.Context, result interface{}, err error) error { + close(unlockNext) + return innerReply(ctx, result, err) + } + _, queueDone := event.Start(ctx, "queued") + go func() { + <-waitForPrevious + queueDone() + if err := handler(ctx, reply, req); err != nil { + event.Error(ctx, "jsonrpc2 async message delivery failed", err) + } + }() + return nil + } +} diff --git a/gopls/golang_org_x_tools/jsonrpc2/jsonrpc2.go b/gopls/golang_org_x_tools/jsonrpc2/jsonrpc2.go new file mode 100644 index 0000000..5a52995 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2/jsonrpc2.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec. +// https://www.jsonrpc.org/specification +// It is intended to be compatible with other implementations at the wire level. +package jsonrpc2 + +const ( + // ErrIdleTimeout is returned when serving timed out waiting for new connections. + ErrIdleTimeout = constError("timed out waiting for new connections") +) + +type constError string + +func (e constError) Error() string { return string(e) } diff --git a/gopls/golang_org_x_tools/jsonrpc2/messages.go b/gopls/golang_org_x_tools/jsonrpc2/messages.go new file mode 100644 index 0000000..58d285d --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2/messages.go @@ -0,0 +1,238 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "encoding/json" + "errors" + "fmt" +) + +// Message is the interface to all jsonrpc2 message types. +// They share no common functionality, but are a closed set of concrete types +// that are allowed to implement this interface. The message types are *Call, +// *Notification and *Response. +type Message interface { + // isJSONRPC2Message is used to make the set of message implementations a + // closed set. + isJSONRPC2Message() +} + +// Request is the shared interface to jsonrpc2 messages that request +// a method be invoked. +// The request types are a closed set of *Call and *Notification. +type Request interface { + Message + // Method is a string containing the method name to invoke. + Method() string + // Params is either a struct or an array with the parameters of the method. + Params() json.RawMessage + // isJSONRPC2Request is used to make the set of request implementations closed. + isJSONRPC2Request() +} + +// Notification is a request for which a response cannot occur, and as such +// it has not ID. +type Notification struct { + // Method is a string containing the method name to invoke. + method string + params json.RawMessage +} + +// Call is a request that expects a response. +// The response will have a matching ID. +type Call struct { + // Method is a string containing the method name to invoke. + method string + // Params is either a struct or an array with the parameters of the method. + params json.RawMessage + // id of this request, used to tie the Response back to the request. + id ID +} + +// Response is a reply to a Call. +// It will have the same ID as the call it is a response to. +type Response struct { + // result is the content of the response. + result json.RawMessage + // err is set only if the call failed. + err error + // ID of the request this is a response to. + id ID +} + +// NewNotification constructs a new Notification message for the supplied +// method and parameters. +func NewNotification(method string, params interface{}) (*Notification, error) { + p, merr := marshalToRaw(params) + return &Notification{method: method, params: p}, merr +} + +func (msg *Notification) Method() string { return msg.method } +func (msg *Notification) Params() json.RawMessage { return msg.params } +func (msg *Notification) isJSONRPC2Message() {} +func (msg *Notification) isJSONRPC2Request() {} + +func (n *Notification) MarshalJSON() ([]byte, error) { + msg := wireRequest{Method: n.method, Params: &n.params} + data, err := json.Marshal(msg) + if err != nil { + return data, fmt.Errorf("marshaling notification: %w", err) + } + return data, nil +} + +func (n *Notification) UnmarshalJSON(data []byte) error { + msg := wireRequest{} + if err := json.Unmarshal(data, &msg); err != nil { + return fmt.Errorf("unmarshaling notification: %w", err) + } + n.method = msg.Method + if msg.Params != nil { + n.params = *msg.Params + } + return nil +} + +// NewCall constructs a new Call message for the supplied ID, method and +// parameters. +func NewCall(id ID, method string, params interface{}) (*Call, error) { + p, merr := marshalToRaw(params) + return &Call{id: id, method: method, params: p}, merr +} + +func (msg *Call) Method() string { return msg.method } +func (msg *Call) Params() json.RawMessage { return msg.params } +func (msg *Call) ID() ID { return msg.id } +func (msg *Call) isJSONRPC2Message() {} +func (msg *Call) isJSONRPC2Request() {} + +func (c *Call) MarshalJSON() ([]byte, error) { + msg := wireRequest{Method: c.method, Params: &c.params, ID: &c.id} + data, err := json.Marshal(msg) + if err != nil { + return data, fmt.Errorf("marshaling call: %w", err) + } + return data, nil +} + +func (c *Call) UnmarshalJSON(data []byte) error { + msg := wireRequest{} + if err := json.Unmarshal(data, &msg); err != nil { + return fmt.Errorf("unmarshaling call: %w", err) + } + c.method = msg.Method + if msg.Params != nil { + c.params = *msg.Params + } + if msg.ID != nil { + c.id = *msg.ID + } + return nil +} + +// NewResponse constructs a new Response message that is a reply to the +// supplied. If err is set result may be ignored. +func NewResponse(id ID, result interface{}, err error) (*Response, error) { + r, merr := marshalToRaw(result) + return &Response{id: id, result: r, err: err}, merr +} + +func (msg *Response) ID() ID { return msg.id } +func (msg *Response) Result() json.RawMessage { return msg.result } +func (msg *Response) Err() error { return msg.err } +func (msg *Response) isJSONRPC2Message() {} + +func (r *Response) MarshalJSON() ([]byte, error) { + msg := &wireResponse{Error: toWireError(r.err), ID: &r.id} + if msg.Error == nil { + msg.Result = &r.result + } + data, err := json.Marshal(msg) + if err != nil { + return data, fmt.Errorf("marshaling notification: %w", err) + } + return data, nil +} + +func toWireError(err error) *wireError { + if err == nil { + // no error, the response is complete + return nil + } + if err, ok := err.(*wireError); ok { + // already a wire error, just use it + return err + } + result := &wireError{Message: err.Error()} + var wrapped *wireError + if errors.As(err, &wrapped) { + // if we wrapped a wire error, keep the code from the wrapped error + // but the message from the outer error + result.Code = wrapped.Code + } + return result +} + +func (r *Response) UnmarshalJSON(data []byte) error { + msg := wireResponse{} + if err := json.Unmarshal(data, &msg); err != nil { + return fmt.Errorf("unmarshaling jsonrpc response: %w", err) + } + if msg.Result != nil { + r.result = *msg.Result + } + if msg.Error != nil { + r.err = msg.Error + } + if msg.ID != nil { + r.id = *msg.ID + } + return nil +} + +func DecodeMessage(data []byte) (Message, error) { + msg := wireCombined{} + if err := json.Unmarshal(data, &msg); err != nil { + return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err) + } + if msg.Method == "" { + // no method, should be a response + if msg.ID == nil { + return nil, ErrInvalidRequest + } + response := &Response{id: *msg.ID} + if msg.Error != nil { + response.err = msg.Error + } + if msg.Result != nil { + response.result = *msg.Result + } + return response, nil + } + // has a method, must be a request + if msg.ID == nil { + // request with no ID is a notify + notify := &Notification{method: msg.Method} + if msg.Params != nil { + notify.params = *msg.Params + } + return notify, nil + } + // request with an ID, must be a call + call := &Call{method: msg.Method, id: *msg.ID} + if msg.Params != nil { + call.params = *msg.Params + } + return call, nil +} + +func marshalToRaw(obj interface{}) (json.RawMessage, error) { + data, err := json.Marshal(obj) + if err != nil { + return json.RawMessage{}, err + } + return json.RawMessage(data), nil +} diff --git a/gopls/golang_org_x_tools/jsonrpc2/serve.go b/gopls/golang_org_x_tools/jsonrpc2/serve.go new file mode 100644 index 0000000..fcb0f1c --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2/serve.go @@ -0,0 +1,163 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "context" + "errors" + "io" + "math" + "net" + "os" + "time" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +// NOTE: This file provides an experimental API for serving multiple remote +// jsonrpc2 clients over the network. For now, it is intentionally similar to +// net/http, but that may change in the future as we figure out the correct +// semantics. + +// A StreamServer is used to serve incoming jsonrpc2 clients communicating over +// a newly created connection. +type StreamServer interface { + ServeStream(context.Context, Conn) error +} + +// The ServerFunc type is an adapter that implements the StreamServer interface +// using an ordinary function. +type ServerFunc func(context.Context, Conn) error + +// ServeStream calls f(ctx, s). +func (f ServerFunc) ServeStream(ctx context.Context, c Conn) error { + return f(ctx, c) +} + +// HandlerServer returns a StreamServer that handles incoming streams using the +// provided handler. +func HandlerServer(h Handler) StreamServer { + return ServerFunc(func(ctx context.Context, conn Conn) error { + conn.Go(ctx, h) + <-conn.Done() + return conn.Err() + }) +} + +// ListenAndServe starts an jsonrpc2 server on the given address. If +// idleTimeout is non-zero, ListenAndServe exits after there are no clients for +// this duration, otherwise it exits only on error. +func ListenAndServe(ctx context.Context, network, addr string, server StreamServer, idleTimeout time.Duration) error { + ln, err := net.Listen(network, addr) + if err != nil { + return err + } + defer ln.Close() + if network == "unix" { + defer os.Remove(addr) + } + return Serve(ctx, ln, server, idleTimeout) +} + +// Serve accepts incoming connections from the network, and handles them using +// the provided server. If idleTimeout is non-zero, ListenAndServe exits after +// there are no clients for this duration, otherwise it exits only on error. +func Serve(ctx context.Context, ln net.Listener, server StreamServer, idleTimeout time.Duration) error { + newConns := make(chan net.Conn) + closedConns := make(chan error) + activeConns := 0 + var acceptErr error + go func() { + defer close(newConns) + for { + var nc net.Conn + nc, acceptErr = ln.Accept() + if acceptErr != nil { + return + } + newConns <- nc + } + }() + + ctx, cancel := context.WithCancel(ctx) + defer func() { + // Signal the Accept goroutine to stop immediately + // and terminate all newly-accepted connections until it returns. + ln.Close() + for nc := range newConns { + nc.Close() + } + // Cancel pending ServeStream callbacks and wait for them to finish. + cancel() + for activeConns > 0 { + err := <-closedConns + if !isClosingError(err) { + event.Error(ctx, "closed a connection", err) + } + activeConns-- + } + }() + + // Max duration: ~290 years; surely that's long enough. + const forever = math.MaxInt64 + if idleTimeout <= 0 { + idleTimeout = forever + } + connTimer := time.NewTimer(idleTimeout) + defer connTimer.Stop() + + for { + select { + case netConn, ok := <-newConns: + if !ok { + return acceptErr + } + if activeConns == 0 && !connTimer.Stop() { + // connTimer.C may receive a value even after Stop returns. + // (See https://golang.org/issue/37196.) + <-connTimer.C + } + activeConns++ + stream := NewHeaderStream(netConn) + go func() { + conn := NewConn(stream) + err := server.ServeStream(ctx, conn) + stream.Close() + closedConns <- err + }() + + case err := <-closedConns: + if !isClosingError(err) { + event.Error(ctx, "closed a connection", err) + } + activeConns-- + if activeConns == 0 { + connTimer.Reset(idleTimeout) + } + + case <-connTimer.C: + return ErrIdleTimeout + + case <-ctx.Done(): + return nil + } + } +} + +// isClosingError reports if the error occurs normally during the process of +// closing a network connection. It uses imperfect heuristics that err on the +// side of false negatives, and should not be used for anything critical. +func isClosingError(err error) bool { + if errors.Is(err, io.EOF) { + return true + } + // Per https://github.com/golang/go/issues/4373, this error string should not + // change. This is not ideal, but since the worst that could happen here is + // some superfluous logging, it is acceptable. + if err.Error() == "use of closed network connection" { + return true + } + return false +} diff --git a/gopls/golang_org_x_tools/jsonrpc2/stream.go b/gopls/golang_org_x_tools/jsonrpc2/stream.go new file mode 100644 index 0000000..b22be7e --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2/stream.go @@ -0,0 +1,170 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "io" + "net" + "strconv" + "strings" +) + +// Stream abstracts the transport mechanics from the JSON RPC protocol. +// A Conn reads and writes messages using the stream it was provided on +// construction, and assumes that each call to Read or Write fully transfers +// a single message, or returns an error. +// A stream is not safe for concurrent use, it is expected it will be used by +// a single Conn in a safe manner. +type Stream interface { + // Read gets the next message from the stream. + Read(context.Context) (Message, int64, error) + // Write sends a message to the stream. + Write(context.Context, Message) (int64, error) + // Close closes the connection. + // Any blocked Read or Write operations will be unblocked and return errors. + Close() error +} + +// Framer wraps a network connection up into a Stream. +// It is responsible for the framing and encoding of messages into wire form. +// NewRawStream and NewHeaderStream are implementations of a Framer. +type Framer func(conn net.Conn) Stream + +// NewRawStream returns a Stream built on top of a net.Conn. +// The messages are sent with no wrapping, and rely on json decode consistency +// to determine message boundaries. +func NewRawStream(conn net.Conn) Stream { + return &rawStream{ + conn: conn, + in: json.NewDecoder(conn), + } +} + +type rawStream struct { + conn net.Conn + in *json.Decoder +} + +func (s *rawStream) Read(ctx context.Context) (Message, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var raw json.RawMessage + if err := s.in.Decode(&raw); err != nil { + return nil, 0, err + } + msg, err := DecodeMessage(raw) + return msg, int64(len(raw)), err +} + +func (s *rawStream) Write(ctx context.Context, msg Message) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + data, err := json.Marshal(msg) + if err != nil { + return 0, fmt.Errorf("marshaling message: %v", err) + } + n, err := s.conn.Write(data) + return int64(n), err +} + +func (s *rawStream) Close() error { + return s.conn.Close() +} + +// NewHeaderStream returns a Stream built on top of a net.Conn. +// The messages are sent with HTTP content length and MIME type headers. +// This is the format used by LSP and others. +func NewHeaderStream(conn net.Conn) Stream { + return &headerStream{ + conn: conn, + in: bufio.NewReader(conn), + } +} + +type headerStream struct { + conn net.Conn + in *bufio.Reader +} + +func (s *headerStream) Read(ctx context.Context) (Message, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var total, length int64 + // read the header, stop on the first empty line + for { + line, err := s.in.ReadString('\n') + total += int64(len(line)) + if err != nil { + return nil, total, fmt.Errorf("failed reading header line: %w", err) + } + line = strings.TrimSpace(line) + // check we have a header line + if line == "" { + break + } + colon := strings.IndexRune(line, ':') + if colon < 0 { + return nil, total, fmt.Errorf("invalid header line %q", line) + } + name, value := line[:colon], strings.TrimSpace(line[colon+1:]) + switch name { + case "Content-Length": + if length, err = strconv.ParseInt(value, 10, 32); err != nil { + return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value) + } + if length <= 0 { + return nil, total, fmt.Errorf("invalid Content-Length: %v", length) + } + default: + // ignoring unknown headers + } + } + if length == 0 { + return nil, total, fmt.Errorf("missing Content-Length header") + } + data := make([]byte, length) + if _, err := io.ReadFull(s.in, data); err != nil { + return nil, total, err + } + total += length + msg, err := DecodeMessage(data) + return msg, total, err +} + +func (s *headerStream) Write(ctx context.Context, msg Message) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + data, err := json.Marshal(msg) + if err != nil { + return 0, fmt.Errorf("marshaling message: %v", err) + } + n, err := fmt.Fprintf(s.conn, "Content-Length: %v\r\n\r\n", len(data)) + total := int64(n) + if err == nil { + n, err = s.conn.Write(data) + total += int64(n) + } + return total, err +} + +func (s *headerStream) Close() error { + return s.conn.Close() +} diff --git a/gopls/golang_org_x_tools/jsonrpc2/wire.go b/gopls/golang_org_x_tools/jsonrpc2/wire.go new file mode 100644 index 0000000..ac39f16 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2/wire.go @@ -0,0 +1,159 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "encoding/json" + "fmt" +) + +// this file contains the go forms of the wire specification +// see http://www.jsonrpc.org/specification for details + +var ( + // ErrUnknown should be used for all non coded errors. + ErrUnknown = NewError(-32001, "JSON RPC unknown error") + // ErrParse is used when invalid JSON was received by the server. + ErrParse = NewError(-32700, "JSON RPC parse error") + //ErrInvalidRequest is used when the JSON sent is not a valid Request object. + ErrInvalidRequest = NewError(-32600, "JSON RPC invalid request") + // ErrMethodNotFound should be returned by the handler when the method does + // not exist / is not available. + ErrMethodNotFound = NewError(-32601, "JSON RPC method not found") + // ErrInvalidParams should be returned by the handler when method + // parameter(s) were invalid. + ErrInvalidParams = NewError(-32602, "JSON RPC invalid params") + // ErrInternal is not currently returned but defined for completeness. + ErrInternal = NewError(-32603, "JSON RPC internal error") + + //ErrServerOverloaded is returned when a message was refused due to a + //server being temporarily unable to accept any new messages. + ErrServerOverloaded = NewError(-32000, "JSON RPC overloaded") +) + +// wireRequest is sent to a server to represent a Call or Notify operation. +type wireRequest struct { + // VersionTag is always encoded as the string "2.0" + VersionTag wireVersionTag `json:"jsonrpc"` + // Method is a string containing the method name to invoke. + Method string `json:"method"` + // Params is either a struct or an array with the parameters of the method. + Params *json.RawMessage `json:"params,omitempty"` + // The id of this request, used to tie the Response back to the request. + // Will be either a string or a number. If not set, the Request is a notify, + // and no response is possible. + ID *ID `json:"id,omitempty"` +} + +// WireResponse is a reply to a Request. +// It will always have the ID field set to tie it back to a request, and will +// have either the Result or Error fields set depending on whether it is a +// success or failure response. +type wireResponse struct { + // VersionTag is always encoded as the string "2.0" + VersionTag wireVersionTag `json:"jsonrpc"` + // Result is the response value, and is required on success. + Result *json.RawMessage `json:"result,omitempty"` + // Error is a structured error response if the call fails. + Error *wireError `json:"error,omitempty"` + // ID must be set and is the identifier of the Request this is a response to. + ID *ID `json:"id,omitempty"` +} + +// wireCombined has all the fields of both Request and Response. +// We can decode this and then work out which it is. +type wireCombined struct { + VersionTag wireVersionTag `json:"jsonrpc"` + ID *ID `json:"id,omitempty"` + Method string `json:"method"` + Params *json.RawMessage `json:"params,omitempty"` + Result *json.RawMessage `json:"result,omitempty"` + Error *wireError `json:"error,omitempty"` +} + +// wireError represents a structured error in a Response. +type wireError struct { + // Code is an error code indicating the type of failure. + Code int64 `json:"code"` + // Message is a short description of the error. + Message string `json:"message"` + // Data is optional structured data containing additional information about the error. + Data *json.RawMessage `json:"data,omitempty"` +} + +// wireVersionTag is a special 0 sized struct that encodes as the jsonrpc version +// tag. +// It will fail during decode if it is not the correct version tag in the +// stream. +type wireVersionTag struct{} + +// ID is a Request identifier. +type ID struct { + name string + number int64 +} + +func NewError(code int64, message string) error { + return &wireError{ + Code: code, + Message: message, + } +} + +func (err *wireError) Error() string { + return err.Message +} + +func (wireVersionTag) MarshalJSON() ([]byte, error) { + return json.Marshal("2.0") +} + +func (wireVersionTag) UnmarshalJSON(data []byte) error { + version := "" + if err := json.Unmarshal(data, &version); err != nil { + return err + } + if version != "2.0" { + return fmt.Errorf("invalid RPC version %v", version) + } + return nil +} + +// NewIntID returns a new numerical request ID. +func NewIntID(v int64) ID { return ID{number: v} } + +// NewStringID returns a new string request ID. +func NewStringID(v string) ID { return ID{name: v} } + +// Format writes the ID to the formatter. +// If the rune is q the representation is non ambiguous, +// string forms are quoted, number forms are preceded by a # +func (id ID) Format(f fmt.State, r rune) { + numF, strF := `%d`, `%s` + if r == 'q' { + numF, strF = `#%d`, `%q` + } + switch { + case id.name != "": + fmt.Fprintf(f, strF, id.name) + default: + fmt.Fprintf(f, numF, id.number) + } +} + +func (id *ID) MarshalJSON() ([]byte, error) { + if id.name != "" { + return json.Marshal(id.name) + } + return json.Marshal(id.number) +} + +func (id *ID) UnmarshalJSON(data []byte) error { + *id = ID{} + if err := json.Unmarshal(data, &id.number); err == nil { + return nil + } + return json.Unmarshal(data, &id.name) +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/conn.go b/gopls/golang_org_x_tools/jsonrpc2_v2/conn.go new file mode 100644 index 0000000..3e690b5 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/conn.go @@ -0,0 +1,812 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "sync" + "sync/atomic" + "time" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/keys" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/tag" +) + +// Binder builds a connection configuration. +// This may be used in servers to generate a new configuration per connection. +// ConnectionOptions itself implements Binder returning itself unmodified, to +// allow for the simple cases where no per connection information is needed. +type Binder interface { + // Bind returns the ConnectionOptions to use when establishing the passed-in + // Connection. + // + // The connection is not ready to use when Bind is called, + // but Bind may close it without reading or writing to it. + Bind(context.Context, *Connection) ConnectionOptions +} + +// A BinderFunc implements the Binder interface for a standalone Bind function. +type BinderFunc func(context.Context, *Connection) ConnectionOptions + +func (f BinderFunc) Bind(ctx context.Context, c *Connection) ConnectionOptions { + return f(ctx, c) +} + +var _ Binder = BinderFunc(nil) + +// ConnectionOptions holds the options for new connections. +type ConnectionOptions struct { + // Framer allows control over the message framing and encoding. + // If nil, HeaderFramer will be used. + Framer Framer + // Preempter allows registration of a pre-queue message handler. + // If nil, no messages will be preempted. + Preempter Preempter + // Handler is used as the queued message handler for inbound messages. + // If nil, all responses will be ErrNotHandled. + Handler Handler + // OnInternalError, if non-nil, is called with any internal errors that occur + // while serving the connection, such as protocol errors or invariant + // violations. (If nil, internal errors result in panics.) + OnInternalError func(error) +} + +// Connection manages the jsonrpc2 protocol, connecting responses back to their +// calls. +// Connection is bidirectional; it does not have a designated server or client +// end. +type Connection struct { + seq int64 // must only be accessed using atomic operations + + stateMu sync.Mutex + state inFlightState // accessed only in updateInFlight + done chan struct{} // closed (under stateMu) when state.closed is true and all goroutines have completed + + writer chan Writer // 1-buffered; stores the writer when not in use + + handler Handler + + onInternalError func(error) + onDone func() +} + +// inFlightState records the state of the incoming and outgoing calls on a +// Connection. +type inFlightState struct { + connClosing bool // true when the Connection's Close method has been called + reading bool // true while the readIncoming goroutine is running + readErr error // non-nil when the readIncoming goroutine exits (typically io.EOF) + writeErr error // non-nil if a call to the Writer has failed with a non-canceled Context + + // closer shuts down and cleans up the Reader and Writer state, ideally + // interrupting any Read or Write call that is currently blocked. It is closed + // when the state is idle and one of: connClosing is true, readErr is non-nil, + // or writeErr is non-nil. + // + // After the closer has been invoked, the closer field is set to nil + // and the closeErr field is simultaneously set to its result. + closer io.Closer + closeErr error // error returned from closer.Close + + outgoingCalls map[ID]*AsyncCall // calls only + outgoingNotifications int // # of notifications awaiting "write" + + // incoming stores the total number of incoming calls and notifications + // that have not yet written or processed a result. + incoming int + + incomingByID map[ID]*incomingRequest // calls only + + // handlerQueue stores the backlog of calls and notifications that were not + // already handled by a preempter. + // The queue does not include the request currently being handled (if any). + handlerQueue []*incomingRequest + handlerRunning bool +} + +// updateInFlight locks the state of the connection's in-flight requests, allows +// f to mutate that state, and closes the connection if it is idle and either +// is closing or has a read or write error. +func (c *Connection) updateInFlight(f func(*inFlightState)) { + c.stateMu.Lock() + defer c.stateMu.Unlock() + + s := &c.state + + f(s) + + select { + case <-c.done: + // The connection was already completely done at the start of this call to + // updateInFlight, so it must remain so. (The call to f should have noticed + // that and avoided making any updates that would cause the state to be + // non-idle.) + if !s.idle() { + panic("jsonrpc2_v2: updateInFlight transitioned to non-idle when already done") + } + return + default: + } + + if s.idle() && s.shuttingDown(ErrUnknown) != nil { + if s.closer != nil { + s.closeErr = s.closer.Close() + s.closer = nil // prevent duplicate Close calls + } + if s.reading { + // The readIncoming goroutine is still running. Our call to Close should + // cause it to exit soon, at which point it will make another call to + // updateInFlight, set s.reading to false, and mark the Connection done. + } else { + // The readIncoming goroutine has exited, or never started to begin with. + // Since everything else is idle, we're completely done. + if c.onDone != nil { + c.onDone() + } + close(c.done) + } + } +} + +// idle reports whether the connction is in a state with no pending calls or +// notifications. +// +// If idle returns true, the readIncoming goroutine may still be running, +// but no other goroutines are doing work on behalf of the connnection. +func (s *inFlightState) idle() bool { + return len(s.outgoingCalls) == 0 && s.outgoingNotifications == 0 && s.incoming == 0 && !s.handlerRunning +} + +// shuttingDown reports whether the connection is in a state that should +// disallow new (incoming and outgoing) calls. It returns either nil or +// an error that is or wraps the provided errClosing. +func (s *inFlightState) shuttingDown(errClosing error) error { + if s.connClosing { + // If Close has been called explicitly, it doesn't matter what state the + // Reader and Writer are in: we shouldn't be starting new work because the + // caller told us not to start new work. + return errClosing + } + if s.readErr != nil { + // If the read side of the connection is broken, we cannot read new call + // requests, and cannot read responses to our outgoing calls. + return fmt.Errorf("%w: %v", errClosing, s.readErr) + } + if s.writeErr != nil { + // If the write side of the connection is broken, we cannot write responses + // for incoming calls, and cannot write requests for outgoing calls. + return fmt.Errorf("%w: %v", errClosing, s.writeErr) + } + return nil +} + +// incomingRequest is used to track an incoming request as it is being handled +type incomingRequest struct { + *Request // the request being processed + ctx context.Context + cancel context.CancelFunc + endSpan func() // called (and set to nil) when the response is sent +} + +// Bind returns the options unmodified. +func (o ConnectionOptions) Bind(context.Context, *Connection) ConnectionOptions { + return o +} + +// newConnection creates a new connection and runs it. +// +// This is used by the Dial and Serve functions to build the actual connection. +// +// The connection is closed automatically (and its resources cleaned up) when +// the last request has completed after the underlying ReadWriteCloser breaks, +// but it may be stopped earlier by calling Close (for a clean shutdown). +func newConnection(bindCtx context.Context, rwc io.ReadWriteCloser, binder Binder, onDone func()) *Connection { + // TODO: Should we create a new event span here? + // This will propagate cancellation from ctx; should it? + ctx := notDone{bindCtx} + + c := &Connection{ + state: inFlightState{closer: rwc}, + done: make(chan struct{}), + writer: make(chan Writer, 1), + onDone: onDone, + } + // It's tempting to set a finalizer on c to verify that the state has gone + // idle when the connection becomes unreachable. Unfortunately, the Binder + // interface makes that unsafe: it allows the Handler to close over the + // Connection, which could create a reference cycle that would cause the + // Connection to become uncollectable. + + options := binder.Bind(bindCtx, c) + framer := options.Framer + if framer == nil { + framer = HeaderFramer() + } + c.handler = options.Handler + if c.handler == nil { + c.handler = defaultHandler{} + } + c.onInternalError = options.OnInternalError + + c.writer <- framer.Writer(rwc) + reader := framer.Reader(rwc) + + c.updateInFlight(func(s *inFlightState) { + select { + case <-c.done: + // Bind already closed the connection; don't start a goroutine to read it. + return + default: + } + + // The goroutine started here will continue until the underlying stream is closed. + // + // (If the Binder closed the Connection already, this should error out and + // return almost immediately.) + s.reading = true + go c.readIncoming(ctx, reader, options.Preempter) + }) + return c +} + +// Notify invokes the target method but does not wait for a response. +// The params will be marshaled to JSON before sending over the wire, and will +// be handed to the method invoked. +func (c *Connection) Notify(ctx context.Context, method string, params interface{}) (err error) { + ctx, done := event.Start(ctx, method, + tag.Method.Of(method), + tag.RPCDirection.Of(tag.Outbound), + ) + attempted := false + + defer func() { + labelStatus(ctx, err) + done() + if attempted { + c.updateInFlight(func(s *inFlightState) { + s.outgoingNotifications-- + }) + } + }() + + c.updateInFlight(func(s *inFlightState) { + // If the connection is shutting down, allow outgoing notifications only if + // there is at least one call still in flight. The number of calls in flight + // cannot increase once shutdown begins, and allowing outgoing notifications + // may permit notifications that will cancel in-flight calls. + if len(s.outgoingCalls) == 0 && len(s.incomingByID) == 0 { + err = s.shuttingDown(ErrClientClosing) + if err != nil { + return + } + } + s.outgoingNotifications++ + attempted = true + }) + if err != nil { + return err + } + + notify, err := NewNotification(method, params) + if err != nil { + return fmt.Errorf("marshaling notify parameters: %v", err) + } + + event.Metric(ctx, tag.Started.Of(1)) + return c.write(ctx, notify) +} + +// Call invokes the target method and returns an object that can be used to await the response. +// The params will be marshaled to JSON before sending over the wire, and will +// be handed to the method invoked. +// You do not have to wait for the response, it can just be ignored if not needed. +// If sending the call failed, the response will be ready and have the error in it. +func (c *Connection) Call(ctx context.Context, method string, params interface{}) *AsyncCall { + // Generate a new request identifier. + id := Int64ID(atomic.AddInt64(&c.seq, 1)) + ctx, endSpan := event.Start(ctx, method, + tag.Method.Of(method), + tag.RPCDirection.Of(tag.Outbound), + tag.RPCID.Of(fmt.Sprintf("%q", id)), + ) + + ac := &AsyncCall{ + id: id, + ready: make(chan struct{}), + ctx: ctx, + endSpan: endSpan, + } + // When this method returns, either ac is retired, or the request has been + // written successfully and the call is awaiting a response (to be provided by + // the readIncoming goroutine). + + call, err := NewCall(ac.id, method, params) + if err != nil { + ac.retire(&Response{ID: id, Error: fmt.Errorf("marshaling call parameters: %w", err)}) + return ac + } + + c.updateInFlight(func(s *inFlightState) { + err = s.shuttingDown(ErrClientClosing) + if err != nil { + return + } + if s.outgoingCalls == nil { + s.outgoingCalls = make(map[ID]*AsyncCall) + } + s.outgoingCalls[ac.id] = ac + }) + if err != nil { + ac.retire(&Response{ID: id, Error: err}) + return ac + } + + event.Metric(ctx, tag.Started.Of(1)) + if err := c.write(ctx, call); err != nil { + // Sending failed. We will never get a response, so deliver a fake one if it + // wasn't already retired by the connection breaking. + c.updateInFlight(func(s *inFlightState) { + if s.outgoingCalls[ac.id] == ac { + delete(s.outgoingCalls, ac.id) + ac.retire(&Response{ID: id, Error: err}) + } else { + // ac was already retired by the readIncoming goroutine: + // perhaps our write raced with the Read side of the connection breaking. + } + }) + } + return ac +} + +type AsyncCall struct { + id ID + ready chan struct{} // closed after response has been set and span has been ended + response *Response + ctx context.Context // for event logging only + endSpan func() // close the tracing span when all processing for the message is complete +} + +// ID used for this call. +// This can be used to cancel the call if needed. +func (ac *AsyncCall) ID() ID { return ac.id } + +// IsReady can be used to check if the result is already prepared. +// This is guaranteed to return true on a result for which Await has already +// returned, or a call that failed to send in the first place. +func (ac *AsyncCall) IsReady() bool { + select { + case <-ac.ready: + return true + default: + return false + } +} + +// retire processes the response to the call. +func (ac *AsyncCall) retire(response *Response) { + select { + case <-ac.ready: + panic(fmt.Sprintf("jsonrpc2: retire called twice for ID %v", ac.id)) + default: + } + + ac.response = response + labelStatus(ac.ctx, response.Error) + ac.endSpan() + // Allow the trace context, which may retain a lot of reachable values, + // to be garbage-collected. + ac.ctx, ac.endSpan = nil, nil + + close(ac.ready) +} + +// Await waits for (and decodes) the results of a Call. +// The response will be unmarshaled from JSON into the result. +func (ac *AsyncCall) Await(ctx context.Context, result interface{}) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ac.ready: + } + if ac.response.Error != nil { + return ac.response.Error + } + if result == nil { + return nil + } + return json.Unmarshal(ac.response.Result, result) +} + +// Respond delivers a response to an incoming Call. +// +// Respond must be called exactly once for any message for which a handler +// returns ErrAsyncResponse. It must not be called for any other message. +func (c *Connection) Respond(id ID, result interface{}, err error) error { + var req *incomingRequest + c.updateInFlight(func(s *inFlightState) { + req = s.incomingByID[id] + }) + if req == nil { + return c.internalErrorf("Request not found for ID %v", id) + } + + if err == ErrAsyncResponse { + // Respond is supposed to supply the asynchronous response, so it would be + // confusing to call Respond with an error that promises to call Respond + // again. + err = c.internalErrorf("Respond called with ErrAsyncResponse for %q", req.Method) + } + return c.processResult("Respond", req, result, err) +} + +// Cancel cancels the Context passed to the Handle call for the inbound message +// with the given ID. +// +// Cancel will not complain if the ID is not a currently active message, and it +// will not cause any messages that have not arrived yet with that ID to be +// cancelled. +func (c *Connection) Cancel(id ID) { + var req *incomingRequest + c.updateInFlight(func(s *inFlightState) { + req = s.incomingByID[id] + }) + if req != nil { + req.cancel() + } +} + +// Wait blocks until the connection is fully closed, but does not close it. +func (c *Connection) Wait() error { + var err error + <-c.done + c.updateInFlight(func(s *inFlightState) { + err = s.closeErr + }) + return err +} + +// Close stops accepting new requests, waits for in-flight requests and enqueued +// Handle calls to complete, and then closes the underlying stream. +// +// After the start of a Close, notification requests (that lack IDs and do not +// receive responses) will continue to be passed to the Preempter, but calls +// with IDs will receive immediate responses with ErrServerClosing, and no new +// requests (not even notifications!) will be enqueued to the Handler. +func (c *Connection) Close() error { + // Stop handling new requests, and interrupt the reader (by closing the + // connection) as soon as the active requests finish. + c.updateInFlight(func(s *inFlightState) { s.connClosing = true }) + + return c.Wait() +} + +// readIncoming collects inbound messages from the reader and delivers them, either responding +// to outgoing calls or feeding requests to the queue. +func (c *Connection) readIncoming(ctx context.Context, reader Reader, preempter Preempter) { + var err error + for { + var ( + msg Message + n int64 + ) + msg, n, err = reader.Read(ctx) + if err != nil { + break + } + + switch msg := msg.(type) { + case *Request: + c.acceptRequest(ctx, msg, n, preempter) + + case *Response: + c.updateInFlight(func(s *inFlightState) { + if ac, ok := s.outgoingCalls[msg.ID]; ok { + delete(s.outgoingCalls, msg.ID) + ac.retire(msg) + } else { + // TODO: How should we report unexpected responses? + } + }) + + default: + c.internalErrorf("Read returned an unexpected message of type %T", msg) + } + } + + c.updateInFlight(func(s *inFlightState) { + s.reading = false + s.readErr = err + + // Retire any outgoing requests that were still in flight: with the Reader no + // longer being processed, they necessarily cannot receive a response. + for id, ac := range s.outgoingCalls { + ac.retire(&Response{ID: id, Error: err}) + } + s.outgoingCalls = nil + }) +} + +// acceptRequest either handles msg synchronously or enqueues it to be handled +// asynchronously. +func (c *Connection) acceptRequest(ctx context.Context, msg *Request, msgBytes int64, preempter Preempter) { + // Add a span to the context for this request. + labels := append(make([]label.Label, 0, 3), // Make space for the ID if present. + tag.Method.Of(msg.Method), + tag.RPCDirection.Of(tag.Inbound), + ) + if msg.IsCall() { + labels = append(labels, tag.RPCID.Of(fmt.Sprintf("%q", msg.ID))) + } + ctx, endSpan := event.Start(ctx, msg.Method, labels...) + event.Metric(ctx, + tag.Started.Of(1), + tag.ReceivedBytes.Of(msgBytes)) + + // In theory notifications cannot be cancelled, but we build them a cancel + // context anyway. + ctx, cancel := context.WithCancel(ctx) + req := &incomingRequest{ + Request: msg, + ctx: ctx, + cancel: cancel, + endSpan: endSpan, + } + + // If the request is a call, add it to the incoming map so it can be + // cancelled (or responded) by ID. + var err error + c.updateInFlight(func(s *inFlightState) { + s.incoming++ + + if req.IsCall() { + if s.incomingByID[req.ID] != nil { + err = fmt.Errorf("%w: request ID %v already in use", ErrInvalidRequest, req.ID) + req.ID = ID{} // Don't misattribute this error to the existing request. + return + } + + if s.incomingByID == nil { + s.incomingByID = make(map[ID]*incomingRequest) + } + s.incomingByID[req.ID] = req + + // When shutting down, reject all new Call requests, even if they could + // theoretically be handled by the preempter. The preempter could return + // ErrAsyncResponse, which would increase the amount of work in flight + // when we're trying to ensure that it strictly decreases. + err = s.shuttingDown(ErrServerClosing) + } + }) + if err != nil { + c.processResult("acceptRequest", req, nil, err) + return + } + + if preempter != nil { + result, err := preempter.Preempt(req.ctx, req.Request) + + if req.IsCall() && errors.Is(err, ErrAsyncResponse) { + // This request will remain in flight until Respond is called for it. + return + } + + if !errors.Is(err, ErrNotHandled) { + c.processResult("Preempt", req, result, err) + return + } + } + + c.updateInFlight(func(s *inFlightState) { + // If the connection is shutting down, don't enqueue anything to the + // handler — not even notifications. That ensures that if the handler + // continues to make progress, it will eventually become idle and + // close the connection. + err = s.shuttingDown(ErrServerClosing) + if err != nil { + return + } + + // We enqueue requests that have not been preempted to an unbounded slice. + // Unfortunately, we cannot in general limit the size of the handler + // queue: we have to read every response that comes in on the wire + // (because it may be responding to a request issued by, say, an + // asynchronous handler), and in order to get to that response we have + // to read all of the requests that came in ahead of it. + s.handlerQueue = append(s.handlerQueue, req) + if !s.handlerRunning { + // We start the handleAsync goroutine when it has work to do, and let it + // exit when the queue empties. + // + // Otherwise, in order to synchronize the handler we would need some other + // goroutine (probably readIncoming?) to explicitly wait for handleAsync + // to finish, and that would complicate error reporting: either the error + // report from the goroutine would be blocked on the handler emptying its + // queue (which was tried, and introduced a deadlock detected by + // TestCloseCallRace), or the error would need to be reported separately + // from synchronizing completion. Allowing the handler goroutine to exit + // when idle seems simpler than trying to implement either of those + // alternatives correctly. + s.handlerRunning = true + go c.handleAsync() + } + }) + if err != nil { + c.processResult("acceptRequest", req, nil, err) + } +} + +// handleAsync invokes the handler on the requests in the handler queue +// sequentially until the queue is empty. +func (c *Connection) handleAsync() { + for { + var req *incomingRequest + c.updateInFlight(func(s *inFlightState) { + if len(s.handlerQueue) > 0 { + req, s.handlerQueue = s.handlerQueue[0], s.handlerQueue[1:] + } else { + s.handlerRunning = false + } + }) + if req == nil { + return + } + + // Only deliver to the Handler if not already canceled. + if err := req.ctx.Err(); err != nil { + c.updateInFlight(func(s *inFlightState) { + if s.writeErr != nil { + // Assume that req.ctx was canceled due to s.writeErr. + // TODO(#51365): use a Context API to plumb this through req.ctx. + err = fmt.Errorf("%w: %v", ErrServerClosing, s.writeErr) + } + }) + c.processResult("handleAsync", req, nil, err) + continue + } + + result, err := c.handler.Handle(req.ctx, req.Request) + c.processResult(c.handler, req, result, err) + } +} + +// processResult processes the result of a request and, if appropriate, sends a response. +func (c *Connection) processResult(from interface{}, req *incomingRequest, result interface{}, err error) error { + switch err { + case ErrAsyncResponse: + if !req.IsCall() { + return c.internalErrorf("%#v returned ErrAsyncResponse for a %q Request without an ID", from, req.Method) + } + return nil // This request is still in flight, so don't record the result yet. + case ErrNotHandled, ErrMethodNotFound: + // Add detail describing the unhandled method. + err = fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method) + } + + if req.endSpan == nil { + return c.internalErrorf("%#v produced a duplicate %q Response", from, req.Method) + } + + if result != nil && err != nil { + c.internalErrorf("%#v returned a non-nil result with a non-nil error for %s:\n%v\n%#v", from, req.Method, err, result) + result = nil // Discard the spurious result and respond with err. + } + + if req.IsCall() { + if result == nil && err == nil { + err = c.internalErrorf("%#v returned a nil result and nil error for a %q Request that requires a Response", from, req.Method) + } + + response, respErr := NewResponse(req.ID, result, err) + + // The caller could theoretically reuse the request's ID as soon as we've + // sent the response, so ensure that it is removed from the incoming map + // before sending. + c.updateInFlight(func(s *inFlightState) { + delete(s.incomingByID, req.ID) + }) + if respErr == nil { + writeErr := c.write(notDone{req.ctx}, response) + if err == nil { + err = writeErr + } + } else { + err = c.internalErrorf("%#v returned a malformed result for %q: %w", from, req.Method, respErr) + } + } else { // req is a notification + if result != nil { + err = c.internalErrorf("%#v returned a non-nil result for a %q Request without an ID", from, req.Method) + } else if err != nil { + err = fmt.Errorf("%w: %q notification failed: %v", ErrInternal, req.Method, err) + } + if err != nil { + // TODO: can/should we do anything with this error beyond writing it to the event log? + // (Is this the right label to attach to the log?) + event.Label(req.ctx, keys.Err.Of(err)) + } + } + + labelStatus(req.ctx, err) + + // Cancel the request and finalize the event span to free any associated resources. + req.cancel() + req.endSpan() + req.endSpan = nil + c.updateInFlight(func(s *inFlightState) { + if s.incoming == 0 { + panic("jsonrpc2_v2: processResult called when incoming count is already zero") + } + s.incoming-- + }) + return nil +} + +// write is used by all things that write outgoing messages, including replies. +// it makes sure that writes are atomic +func (c *Connection) write(ctx context.Context, msg Message) error { + writer := <-c.writer + defer func() { c.writer <- writer }() + n, err := writer.Write(ctx, msg) + event.Metric(ctx, tag.SentBytes.Of(n)) + + if err != nil && ctx.Err() == nil { + // The call to Write failed, and since ctx.Err() is nil we can't attribute + // the failure (even indirectly) to Context cancellation. The writer appears + // to be broken, and future writes are likely to also fail. + // + // If the read side of the connection is also broken, we might not even be + // able to receive cancellation notifications. Since we can't reliably write + // the results of incoming calls and can't receive explicit cancellations, + // cancel the calls now. + c.updateInFlight(func(s *inFlightState) { + if s.writeErr == nil { + s.writeErr = err + for _, r := range s.incomingByID { + r.cancel() + } + } + }) + } + + return err +} + +// internalErrorf reports an internal error. By default it panics, but if +// c.onInternalError is non-nil it instead calls that and returns an error +// wrapping ErrInternal. +func (c *Connection) internalErrorf(format string, args ...interface{}) error { + err := fmt.Errorf(format, args...) + if c.onInternalError == nil { + panic("jsonrpc2: " + err.Error()) + } + c.onInternalError(err) + + return fmt.Errorf("%w: %v", ErrInternal, err) +} + +// labelStatus labels the status of the event in ctx based on whether err is nil. +func labelStatus(ctx context.Context, err error) { + if err == nil { + event.Label(ctx, tag.StatusCode.Of("OK")) + } else { + event.Label(ctx, tag.StatusCode.Of("ERROR")) + } +} + +// notDone is a context.Context wrapper that returns a nil Done channel. +type notDone struct{ ctx context.Context } + +func (ic notDone) Value(key interface{}) interface{} { + return ic.ctx.Value(key) +} + +func (notDone) Done() <-chan struct{} { return nil } +func (notDone) Err() error { return nil } +func (notDone) Deadline() (time.Time, bool) { return time.Time{}, false } diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/frame.go b/gopls/golang_org_x_tools/jsonrpc2_v2/frame.go new file mode 100644 index 0000000..e424832 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/frame.go @@ -0,0 +1,183 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "io" + "strconv" + "strings" +) + +// Reader abstracts the transport mechanics from the JSON RPC protocol. +// A Conn reads messages from the reader it was provided on construction, +// and assumes that each call to Read fully transfers a single message, +// or returns an error. +// A reader is not safe for concurrent use, it is expected it will be used by +// a single Conn in a safe manner. +type Reader interface { + // Read gets the next message from the stream. + Read(context.Context) (Message, int64, error) +} + +// Writer abstracts the transport mechanics from the JSON RPC protocol. +// A Conn writes messages using the writer it was provided on construction, +// and assumes that each call to Write fully transfers a single message, +// or returns an error. +// A writer is not safe for concurrent use, it is expected it will be used by +// a single Conn in a safe manner. +type Writer interface { + // Write sends a message to the stream. + Write(context.Context, Message) (int64, error) +} + +// Framer wraps low level byte readers and writers into jsonrpc2 message +// readers and writers. +// It is responsible for the framing and encoding of messages into wire form. +type Framer interface { + // Reader wraps a byte reader into a message reader. + Reader(rw io.Reader) Reader + // Writer wraps a byte writer into a message writer. + Writer(rw io.Writer) Writer +} + +// RawFramer returns a new Framer. +// The messages are sent with no wrapping, and rely on json decode consistency +// to determine message boundaries. +func RawFramer() Framer { return rawFramer{} } + +type rawFramer struct{} +type rawReader struct{ in *json.Decoder } +type rawWriter struct{ out io.Writer } + +func (rawFramer) Reader(rw io.Reader) Reader { + return &rawReader{in: json.NewDecoder(rw)} +} + +func (rawFramer) Writer(rw io.Writer) Writer { + return &rawWriter{out: rw} +} + +func (r *rawReader) Read(ctx context.Context) (Message, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var raw json.RawMessage + if err := r.in.Decode(&raw); err != nil { + return nil, 0, err + } + msg, err := DecodeMessage(raw) + return msg, int64(len(raw)), err +} + +func (w *rawWriter) Write(ctx context.Context, msg Message) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + data, err := EncodeMessage(msg) + if err != nil { + return 0, fmt.Errorf("marshaling message: %v", err) + } + n, err := w.out.Write(data) + return int64(n), err +} + +// HeaderFramer returns a new Framer. +// The messages are sent with HTTP content length and MIME type headers. +// This is the format used by LSP and others. +func HeaderFramer() Framer { return headerFramer{} } + +type headerFramer struct{} +type headerReader struct{ in *bufio.Reader } +type headerWriter struct{ out io.Writer } + +func (headerFramer) Reader(rw io.Reader) Reader { + return &headerReader{in: bufio.NewReader(rw)} +} + +func (headerFramer) Writer(rw io.Writer) Writer { + return &headerWriter{out: rw} +} + +func (r *headerReader) Read(ctx context.Context) (Message, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var total, length int64 + // read the header, stop on the first empty line + for { + line, err := r.in.ReadString('\n') + total += int64(len(line)) + if err != nil { + if err == io.EOF { + if total == 0 { + return nil, 0, io.EOF + } + err = io.ErrUnexpectedEOF + } + return nil, total, fmt.Errorf("failed reading header line: %w", err) + } + line = strings.TrimSpace(line) + // check we have a header line + if line == "" { + break + } + colon := strings.IndexRune(line, ':') + if colon < 0 { + return nil, total, fmt.Errorf("invalid header line %q", line) + } + name, value := line[:colon], strings.TrimSpace(line[colon+1:]) + switch name { + case "Content-Length": + if length, err = strconv.ParseInt(value, 10, 32); err != nil { + return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value) + } + if length <= 0 { + return nil, total, fmt.Errorf("invalid Content-Length: %v", length) + } + default: + // ignoring unknown headers + } + } + if length == 0 { + return nil, total, fmt.Errorf("missing Content-Length header") + } + data := make([]byte, length) + n, err := io.ReadFull(r.in, data) + total += int64(n) + if err != nil { + return nil, total, err + } + msg, err := DecodeMessage(data) + return msg, total, err +} + +func (w *headerWriter) Write(ctx context.Context, msg Message) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + data, err := EncodeMessage(msg) + if err != nil { + return 0, fmt.Errorf("marshaling message: %v", err) + } + n, err := fmt.Fprintf(w.out, "Content-Length: %v\r\n\r\n", len(data)) + total := int64(n) + if err == nil { + n, err = w.out.Write(data) + total += int64(n) + } + return total, err +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/jsonrpc2.go b/gopls/golang_org_x_tools/jsonrpc2_v2/jsonrpc2.go new file mode 100644 index 0000000..e9164b0 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/jsonrpc2.go @@ -0,0 +1,137 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec. +// https://www.jsonrpc.org/specification +// It is intended to be compatible with other implementations at the wire level. +package jsonrpc2 + +import ( + "context" + "errors" +) + +var ( + // ErrIdleTimeout is returned when serving timed out waiting for new connections. + ErrIdleTimeout = errors.New("timed out waiting for new connections") + + // ErrNotHandled is returned from a Handler or Preempter to indicate it did + // not handle the request. + // + // If a Handler returns ErrNotHandled, the server replies with + // ErrMethodNotFound. + ErrNotHandled = errors.New("JSON RPC not handled") + + // ErrAsyncResponse is returned from a handler to indicate it will generate a + // response asynchronously. + // + // ErrAsyncResponse must not be returned for notifications, + // which do not receive responses. + ErrAsyncResponse = errors.New("JSON RPC asynchronous response") +) + +// Preempter handles messages on a connection before they are queued to the main +// handler. +// Primarily this is used for cancel handlers or notifications for which out of +// order processing is not an issue. +type Preempter interface { + // Preempt is invoked for each incoming request before it is queued for handling. + // + // If Preempt returns ErrNotHandled, the request will be queued, + // and eventually passed to a Handle call. + // + // Otherwise, the result and error are processed as if returned by Handle. + // + // Preempt must not block. (The Context passed to it is for Values only.) + Preempt(ctx context.Context, req *Request) (result interface{}, err error) +} + +// A PreempterFunc implements the Preempter interface for a standalone Preempt function. +type PreempterFunc func(ctx context.Context, req *Request) (interface{}, error) + +func (f PreempterFunc) Preempt(ctx context.Context, req *Request) (interface{}, error) { + return f(ctx, req) +} + +var _ Preempter = PreempterFunc(nil) + +// Handler handles messages on a connection. +type Handler interface { + // Handle is invoked sequentially for each incoming request that has not + // already been handled by a Preempter. + // + // If the Request has a nil ID, Handle must return a nil result, + // and any error may be logged but will not be reported to the caller. + // + // If the Request has a non-nil ID, Handle must return either a + // non-nil, JSON-marshalable result, or a non-nil error. + // + // The Context passed to Handle will be canceled if the + // connection is broken or the request is canceled or completed. + // (If Handle returns ErrAsyncResponse, ctx will remain uncanceled + // until either Cancel or Respond is called for the request's ID.) + Handle(ctx context.Context, req *Request) (result interface{}, err error) +} + +type defaultHandler struct{} + +func (defaultHandler) Preempt(context.Context, *Request) (interface{}, error) { + return nil, ErrNotHandled +} + +func (defaultHandler) Handle(context.Context, *Request) (interface{}, error) { + return nil, ErrNotHandled +} + +// A HandlerFunc implements the Handler interface for a standalone Handle function. +type HandlerFunc func(ctx context.Context, req *Request) (interface{}, error) + +func (f HandlerFunc) Handle(ctx context.Context, req *Request) (interface{}, error) { + return f(ctx, req) +} + +var _ Handler = HandlerFunc(nil) + +// async is a small helper for operations with an asynchronous result that you +// can wait for. +type async struct { + ready chan struct{} // closed when done + firstErr chan error // 1-buffered; contains either nil or the first non-nil error +} + +func newAsync() *async { + var a async + a.ready = make(chan struct{}) + a.firstErr = make(chan error, 1) + a.firstErr <- nil + return &a +} + +func (a *async) done() { + close(a.ready) +} + +func (a *async) isDone() bool { + select { + case <-a.ready: + return true + default: + return false + } +} + +func (a *async) wait() error { + <-a.ready + err := <-a.firstErr + a.firstErr <- err + return err +} + +func (a *async) setError(err error) { + storedErr := <-a.firstErr + if storedErr == nil { + storedErr = err + } + a.firstErr <- storedErr +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/messages.go b/gopls/golang_org_x_tools/jsonrpc2_v2/messages.go new file mode 100644 index 0000000..af14564 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/messages.go @@ -0,0 +1,181 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "encoding/json" + "errors" + "fmt" +) + +// ID is a Request identifier. +type ID struct { + value interface{} +} + +// Message is the interface to all jsonrpc2 message types. +// They share no common functionality, but are a closed set of concrete types +// that are allowed to implement this interface. The message types are *Request +// and *Response. +type Message interface { + // marshal builds the wire form from the API form. + // It is private, which makes the set of Message implementations closed. + marshal(to *wireCombined) +} + +// Request is a Message sent to a peer to request behavior. +// If it has an ID it is a call, otherwise it is a notification. +type Request struct { + // ID of this request, used to tie the Response back to the request. + // This will be nil for notifications. + ID ID + // Method is a string containing the method name to invoke. + Method string + // Params is either a struct or an array with the parameters of the method. + Params json.RawMessage +} + +// Response is a Message used as a reply to a call Request. +// It will have the same ID as the call it is a response to. +type Response struct { + // result is the content of the response. + Result json.RawMessage + // err is set only if the call failed. + Error error + // id of the request this is a response to. + ID ID +} + +// StringID creates a new string request identifier. +func StringID(s string) ID { return ID{value: s} } + +// Int64ID creates a new integer request identifier. +func Int64ID(i int64) ID { return ID{value: i} } + +// IsValid returns true if the ID is a valid identifier. +// The default value for ID will return false. +func (id ID) IsValid() bool { return id.value != nil } + +// Raw returns the underlying value of the ID. +func (id ID) Raw() interface{} { return id.value } + +// NewNotification constructs a new Notification message for the supplied +// method and parameters. +func NewNotification(method string, params interface{}) (*Request, error) { + p, merr := marshalToRaw(params) + return &Request{Method: method, Params: p}, merr +} + +// NewCall constructs a new Call message for the supplied ID, method and +// parameters. +func NewCall(id ID, method string, params interface{}) (*Request, error) { + p, merr := marshalToRaw(params) + return &Request{ID: id, Method: method, Params: p}, merr +} + +func (msg *Request) IsCall() bool { return msg.ID.IsValid() } + +func (msg *Request) marshal(to *wireCombined) { + to.ID = msg.ID.value + to.Method = msg.Method + to.Params = msg.Params +} + +// NewResponse constructs a new Response message that is a reply to the +// supplied. If err is set result may be ignored. +func NewResponse(id ID, result interface{}, rerr error) (*Response, error) { + r, merr := marshalToRaw(result) + return &Response{ID: id, Result: r, Error: rerr}, merr +} + +func (msg *Response) marshal(to *wireCombined) { + to.ID = msg.ID.value + to.Error = toWireError(msg.Error) + to.Result = msg.Result +} + +func toWireError(err error) *wireError { + if err == nil { + // no error, the response is complete + return nil + } + if err, ok := err.(*wireError); ok { + // already a wire error, just use it + return err + } + result := &wireError{Message: err.Error()} + var wrapped *wireError + if errors.As(err, &wrapped) { + // if we wrapped a wire error, keep the code from the wrapped error + // but the message from the outer error + result.Code = wrapped.Code + } + return result +} + +func EncodeMessage(msg Message) ([]byte, error) { + wire := wireCombined{VersionTag: wireVersion} + msg.marshal(&wire) + data, err := json.Marshal(&wire) + if err != nil { + return data, fmt.Errorf("marshaling jsonrpc message: %w", err) + } + return data, nil +} + +func DecodeMessage(data []byte) (Message, error) { + msg := wireCombined{} + if err := json.Unmarshal(data, &msg); err != nil { + return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err) + } + if msg.VersionTag != wireVersion { + return nil, fmt.Errorf("invalid message version tag %s expected %s", msg.VersionTag, wireVersion) + } + id := ID{} + switch v := msg.ID.(type) { + case nil: + case float64: + // coerce the id type to int64 if it is float64, the spec does not allow fractional parts + id = Int64ID(int64(v)) + case int64: + id = Int64ID(v) + case string: + id = StringID(v) + default: + return nil, fmt.Errorf("invalid message id type <%T>%v", v, v) + } + if msg.Method != "" { + // has a method, must be a call + return &Request{ + Method: msg.Method, + ID: id, + Params: msg.Params, + }, nil + } + // no method, should be a response + if !id.IsValid() { + return nil, ErrInvalidRequest + } + resp := &Response{ + ID: id, + Result: msg.Result, + } + // we have to check if msg.Error is nil to avoid a typed error + if msg.Error != nil { + resp.Error = msg.Error + } + return resp, nil +} + +func marshalToRaw(obj interface{}) (json.RawMessage, error) { + if obj == nil { + return nil, nil + } + data, err := json.Marshal(obj) + if err != nil { + return nil, err + } + return json.RawMessage(data), nil +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/net.go b/gopls/golang_org_x_tools/jsonrpc2_v2/net.go new file mode 100644 index 0000000..15d0aea --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/net.go @@ -0,0 +1,138 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "context" + "io" + "net" + "os" +) + +// This file contains implementations of the transport primitives that use the standard network +// package. + +// NetListenOptions is the optional arguments to the NetListen function. +type NetListenOptions struct { + NetListenConfig net.ListenConfig + NetDialer net.Dialer +} + +// NetListener returns a new Listener that listens on a socket using the net package. +func NetListener(ctx context.Context, network, address string, options NetListenOptions) (Listener, error) { + ln, err := options.NetListenConfig.Listen(ctx, network, address) + if err != nil { + return nil, err + } + return &netListener{net: ln}, nil +} + +// netListener is the implementation of Listener for connections made using the net package. +type netListener struct { + net net.Listener +} + +// Accept blocks waiting for an incoming connection to the listener. +func (l *netListener) Accept(context.Context) (io.ReadWriteCloser, error) { + return l.net.Accept() +} + +// Close will cause the listener to stop listening. It will not close any connections that have +// already been accepted. +func (l *netListener) Close() error { + addr := l.net.Addr() + err := l.net.Close() + if addr.Network() == "unix" { + rerr := os.Remove(addr.String()) + if rerr != nil && err == nil { + err = rerr + } + } + return err +} + +// Dialer returns a dialer that can be used to connect to the listener. +func (l *netListener) Dialer() Dialer { + return NetDialer(l.net.Addr().Network(), l.net.Addr().String(), net.Dialer{}) +} + +// NetDialer returns a Dialer using the supplied standard network dialer. +func NetDialer(network, address string, nd net.Dialer) Dialer { + return &netDialer{ + network: network, + address: address, + dialer: nd, + } +} + +type netDialer struct { + network string + address string + dialer net.Dialer +} + +func (n *netDialer) Dial(ctx context.Context) (io.ReadWriteCloser, error) { + return n.dialer.DialContext(ctx, n.network, n.address) +} + +// NetPipeListener returns a new Listener that listens using net.Pipe. +// It is only possibly to connect to it using the Dialer returned by the +// Dialer method, each call to that method will generate a new pipe the other +// side of which will be returned from the Accept call. +func NetPipeListener(ctx context.Context) (Listener, error) { + return &netPiper{ + done: make(chan struct{}), + dialed: make(chan io.ReadWriteCloser), + }, nil +} + +// netPiper is the implementation of Listener build on top of net.Pipes. +type netPiper struct { + done chan struct{} + dialed chan io.ReadWriteCloser +} + +// Accept blocks waiting for an incoming connection to the listener. +func (l *netPiper) Accept(context.Context) (io.ReadWriteCloser, error) { + // Block until the pipe is dialed or the listener is closed, + // preferring the latter if already closed at the start of Accept. + select { + case <-l.done: + return nil, errClosed + default: + } + select { + case rwc := <-l.dialed: + return rwc, nil + case <-l.done: + return nil, errClosed + } +} + +// Close will cause the listener to stop listening. It will not close any connections that have +// already been accepted. +func (l *netPiper) Close() error { + // unblock any accept calls that are pending + close(l.done) + return nil +} + +func (l *netPiper) Dialer() Dialer { + return l +} + +func (l *netPiper) Dial(ctx context.Context) (io.ReadWriteCloser, error) { + client, server := net.Pipe() + + select { + case l.dialed <- server: + return client, nil + + case <-l.done: + client.Close() + server.Close() + return nil, errClosed + } +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/serve.go b/gopls/golang_org_x_tools/jsonrpc2_v2/serve.go new file mode 100644 index 0000000..7df7856 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/serve.go @@ -0,0 +1,328 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "context" + "fmt" + "io" + "runtime" + "sync" + "sync/atomic" + "time" +) + +// Listener is implemented by protocols to accept new inbound connections. +type Listener interface { + // Accept accepts an inbound connection to a server. + // It blocks until either an inbound connection is made, or the listener is closed. + Accept(context.Context) (io.ReadWriteCloser, error) + + // Close closes the listener. + // Any blocked Accept or Dial operations will unblock and return errors. + Close() error + + // Dialer returns a dialer that can be used to connect to this listener + // locally. + // If a listener does not implement this it will return nil. + Dialer() Dialer +} + +// Dialer is used by clients to dial a server. +type Dialer interface { + // Dial returns a new communication byte stream to a listening server. + Dial(ctx context.Context) (io.ReadWriteCloser, error) +} + +// Server is a running server that is accepting incoming connections. +type Server struct { + listener Listener + binder Binder + async *async + + shutdownOnce sync.Once + closing int32 // atomic: set to nonzero when Shutdown is called +} + +// Dial uses the dialer to make a new connection, wraps the returned +// reader and writer using the framer to make a stream, and then builds +// a connection on top of that stream using the binder. +// +// The returned Connection will operate independently using the Preempter and/or +// Handler provided by the Binder, and will release its own resources when the +// connection is broken, but the caller may Close it earlier to stop accepting +// (or sending) new requests. +func Dial(ctx context.Context, dialer Dialer, binder Binder) (*Connection, error) { + // dial a server + rwc, err := dialer.Dial(ctx) + if err != nil { + return nil, err + } + return newConnection(ctx, rwc, binder, nil), nil +} + +// NewServer starts a new server listening for incoming connections and returns +// it. +// This returns a fully running and connected server, it does not block on +// the listener. +// You can call Wait to block on the server, or Shutdown to get the sever to +// terminate gracefully. +// To notice incoming connections, use an intercepting Binder. +func NewServer(ctx context.Context, listener Listener, binder Binder) *Server { + server := &Server{ + listener: listener, + binder: binder, + async: newAsync(), + } + go server.run(ctx) + return server +} + +// Wait returns only when the server has shut down. +func (s *Server) Wait() error { + return s.async.wait() +} + +// Shutdown informs the server to stop accepting new connections. +func (s *Server) Shutdown() { + s.shutdownOnce.Do(func() { + atomic.StoreInt32(&s.closing, 1) + s.listener.Close() + }) +} + +// run accepts incoming connections from the listener, +// If IdleTimeout is non-zero, run exits after there are no clients for this +// duration, otherwise it exits only on error. +func (s *Server) run(ctx context.Context) { + defer s.async.done() + + var activeConns sync.WaitGroup + for { + rwc, err := s.listener.Accept(ctx) + if err != nil { + // Only Shutdown closes the listener. If we get an error after Shutdown is + // called, assume that that was the cause and don't report the error; + // otherwise, report the error in case it is unexpected. + if atomic.LoadInt32(&s.closing) == 0 { + s.async.setError(err) + } + // We are done generating new connections for good. + break + } + + // A new inbound connection. + activeConns.Add(1) + _ = newConnection(ctx, rwc, s.binder, activeConns.Done) // unregisters itself when done + } + activeConns.Wait() +} + +// NewIdleListener wraps a listener with an idle timeout. +// +// When there are no active connections for at least the timeout duration, +// calls to Accept will fail with ErrIdleTimeout. +// +// A connection is considered inactive as soon as its Close method is called. +func NewIdleListener(timeout time.Duration, wrap Listener) Listener { + l := &idleListener{ + wrapped: wrap, + timeout: timeout, + active: make(chan int, 1), + timedOut: make(chan struct{}), + idleTimer: make(chan *time.Timer, 1), + } + l.idleTimer <- time.AfterFunc(l.timeout, l.timerExpired) + return l +} + +type idleListener struct { + wrapped Listener + timeout time.Duration + + // Only one of these channels is receivable at any given time. + active chan int // count of active connections; closed when Close is called if not timed out + timedOut chan struct{} // closed when the idle timer expires + idleTimer chan *time.Timer // holds the timer only when idle +} + +// Accept accepts an incoming connection. +// +// If an incoming connection is accepted concurrent to the listener being closed +// due to idleness, the new connection is immediately closed. +func (l *idleListener) Accept(ctx context.Context) (io.ReadWriteCloser, error) { + rwc, err := l.wrapped.Accept(ctx) + + select { + case n, ok := <-l.active: + if err != nil { + if ok { + l.active <- n + } + return nil, err + } + if ok { + l.active <- n + 1 + } else { + // l.wrapped.Close Close has been called, but Accept returned a + // connection. This race can occur with concurrent Accept and Close calls + // with any net.Listener, and it is benign: since the listener was closed + // explicitly, it can't have also timed out. + } + return l.newConn(rwc), nil + + case <-l.timedOut: + if err == nil { + // Keeping the connection open would leave the listener simultaneously + // active and closed due to idleness, which would be contradictory and + // confusing. Close the connection and pretend that it never happened. + rwc.Close() + } else { + // In theory the timeout could have raced with an unrelated error return + // from Accept. However, ErrIdleTimeout is arguably still valid (since we + // would have closed due to the timeout independent of the error), and the + // harm from returning a spurious ErrIdleTimeout is negliglible anyway. + } + return nil, ErrIdleTimeout + + case timer := <-l.idleTimer: + if err != nil { + // The idle timer doesn't run until it receives itself from the idleTimer + // channel, so it can't have called l.wrapped.Close yet and thus err can't + // be ErrIdleTimeout. Leave the idle timer as it was and return whatever + // error we got. + l.idleTimer <- timer + return nil, err + } + + if !timer.Stop() { + // Failed to stop the timer — the timer goroutine is in the process of + // firing. Send the timer back to the timer goroutine so that it can + // safely close the timedOut channel, and then wait for the listener to + // actually be closed before we return ErrIdleTimeout. + l.idleTimer <- timer + rwc.Close() + <-l.timedOut + return nil, ErrIdleTimeout + } + + l.active <- 1 + return l.newConn(rwc), nil + } +} + +func (l *idleListener) Close() error { + select { + case _, ok := <-l.active: + if ok { + close(l.active) + } + + case <-l.timedOut: + // Already closed by the timer; take care not to double-close if the caller + // only explicitly invokes this Close method once, since the io.Closer + // interface explicitly leaves doubled Close calls undefined. + return ErrIdleTimeout + + case timer := <-l.idleTimer: + if !timer.Stop() { + // Couldn't stop the timer. It shouldn't take long to run, so just wait + // (so that the Listener is guaranteed to be closed before we return) + // and pretend that this call happened afterward. + // That way we won't leak any timers or goroutines when Close returns. + l.idleTimer <- timer + <-l.timedOut + return ErrIdleTimeout + } + close(l.active) + } + + return l.wrapped.Close() +} + +func (l *idleListener) Dialer() Dialer { + return l.wrapped.Dialer() +} + +func (l *idleListener) timerExpired() { + select { + case n, ok := <-l.active: + if ok { + panic(fmt.Sprintf("jsonrpc2: idleListener idle timer fired with %d connections still active", n)) + } else { + panic("jsonrpc2: Close finished with idle timer still running") + } + + case <-l.timedOut: + panic("jsonrpc2: idleListener idle timer fired more than once") + + case <-l.idleTimer: + // The timer for this very call! + } + + // Close the Listener with all channels still blocked to ensure that this call + // to l.wrapped.Close doesn't race with the one in l.Close. + defer close(l.timedOut) + l.wrapped.Close() +} + +func (l *idleListener) connClosed() { + select { + case n, ok := <-l.active: + if !ok { + // l is already closed, so it can't close due to idleness, + // and we don't need to track the number of active connections any more. + return + } + n-- + if n == 0 { + l.idleTimer <- time.AfterFunc(l.timeout, l.timerExpired) + } else { + l.active <- n + } + + case <-l.timedOut: + panic("jsonrpc2: idleListener idle timer fired before last active connection was closed") + + case <-l.idleTimer: + panic("jsonrpc2: idleListener idle timer active before last active connection was closed") + } +} + +type idleListenerConn struct { + wrapped io.ReadWriteCloser + l *idleListener + closeOnce sync.Once +} + +func (l *idleListener) newConn(rwc io.ReadWriteCloser) *idleListenerConn { + c := &idleListenerConn{ + wrapped: rwc, + l: l, + } + + // A caller that forgets to call Close may disrupt the idleListener's + // accounting, even though the file descriptor for the underlying connection + // may eventually be garbage-collected anyway. + // + // Set a (best-effort) finalizer to verify that a Close call always occurs. + // (We will clear the finalizer explicitly in Close.) + runtime.SetFinalizer(c, func(c *idleListenerConn) { + panic("jsonrpc2: IdleListener connection became unreachable without a call to Close") + }) + + return c +} + +func (c *idleListenerConn) Read(p []byte) (int, error) { return c.wrapped.Read(p) } +func (c *idleListenerConn) Write(p []byte) (int, error) { return c.wrapped.Write(p) } + +func (c *idleListenerConn) Close() error { + defer c.closeOnce.Do(func() { + c.l.connClosed() + runtime.SetFinalizer(c, nil) + }) + return c.wrapped.Close() +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/serve_go116.go b/gopls/golang_org_x_tools/jsonrpc2_v2/serve_go116.go new file mode 100644 index 0000000..29549f1 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/serve_go116.go @@ -0,0 +1,19 @@ +// 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.16 +// +build go1.16 + +package jsonrpc2 + +import ( + "errors" + "net" +) + +var errClosed = net.ErrClosed + +func isErrClosed(err error) bool { + return errors.Is(err, errClosed) +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/serve_pre116.go b/gopls/golang_org_x_tools/jsonrpc2_v2/serve_pre116.go new file mode 100644 index 0000000..14afa83 --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/serve_pre116.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.16 +// +build !go1.16 + +package jsonrpc2 + +import ( + "errors" + "strings" +) + +// errClosed is an error with the same string as net.ErrClosed, +// which was added in Go 1.16. +var errClosed = errors.New("use of closed network connection") + +// isErrClosed reports whether err ends in the same string as errClosed. +func isErrClosed(err error) bool { + // As of Go 1.16, this could be 'errors.Is(err, net.ErrClosing)', but + // unfortunately gopls still requires compatiblity with + // (otherwise-unsupported) older Go versions. + // + // In the meantime, this error strirng has not changed on any supported Go + // version, and is not expected to change in the future. + // This is not ideal, but since the worst that could happen here is some + // superfluous logging, it is acceptable. + return strings.HasSuffix(err.Error(), "use of closed network connection") +} diff --git a/gopls/golang_org_x_tools/jsonrpc2_v2/wire.go b/gopls/golang_org_x_tools/jsonrpc2_v2/wire.go new file mode 100644 index 0000000..c8dc9eb --- /dev/null +++ b/gopls/golang_org_x_tools/jsonrpc2_v2/wire.go @@ -0,0 +1,86 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "encoding/json" +) + +// This file contains the go forms of the wire specification. +// see http://www.jsonrpc.org/specification for details + +var ( + // ErrParse is used when invalid JSON was received by the server. + ErrParse = NewError(-32700, "JSON RPC parse error") + // ErrInvalidRequest is used when the JSON sent is not a valid Request object. + ErrInvalidRequest = NewError(-32600, "JSON RPC invalid request") + // ErrMethodNotFound should be returned by the handler when the method does + // not exist / is not available. + ErrMethodNotFound = NewError(-32601, "JSON RPC method not found") + // ErrInvalidParams should be returned by the handler when method + // parameter(s) were invalid. + ErrInvalidParams = NewError(-32602, "JSON RPC invalid params") + // ErrInternal indicates a failure to process a call correctly + ErrInternal = NewError(-32603, "JSON RPC internal error") + + // The following errors are not part of the json specification, but + // compliant extensions specific to this implementation. + + // ErrServerOverloaded is returned when a message was refused due to a + // server being temporarily unable to accept any new messages. + ErrServerOverloaded = NewError(-32000, "JSON RPC overloaded") + // ErrUnknown should be used for all non coded errors. + ErrUnknown = NewError(-32001, "JSON RPC unknown error") + // ErrServerClosing is returned for calls that arrive while the server is closing. + ErrServerClosing = NewError(-32002, "JSON RPC server is closing") + // ErrClientClosing is a dummy error returned for calls initiated while the client is closing. + ErrClientClosing = NewError(-32003, "JSON RPC client is closing") +) + +const wireVersion = "2.0" + +// wireCombined has all the fields of both Request and Response. +// We can decode this and then work out which it is. +type wireCombined struct { + VersionTag string `json:"jsonrpc"` + ID interface{} `json:"id,omitempty"` + Method string `json:"method,omitempty"` + Params json.RawMessage `json:"params,omitempty"` + Result json.RawMessage `json:"result,omitempty"` + Error *wireError `json:"error,omitempty"` +} + +// wireError represents a structured error in a Response. +type wireError struct { + // Code is an error code indicating the type of failure. + Code int64 `json:"code"` + // Message is a short description of the error. + Message string `json:"message"` + // Data is optional structured data containing additional information about the error. + Data json.RawMessage `json:"data,omitempty"` +} + +// NewError returns an error that will encode on the wire correctly. +// The standard codes are made available from this package, this function should +// only be used to build errors for application specific codes as allowed by the +// specification. +func NewError(code int64, message string) error { + return &wireError{ + Code: code, + Message: message, + } +} + +func (err *wireError) Error() string { + return err.Message +} + +func (err *wireError) Is(other error) bool { + w, ok := other.(*wireError) + if !ok { + return false + } + return err.Code == w.Code +} diff --git a/gopls/golang_org_x_tools/packagesinternal/packages.go b/gopls/golang_org_x_tools/packagesinternal/packages.go new file mode 100644 index 0000000..82a2c9b --- /dev/null +++ b/gopls/golang_org_x_tools/packagesinternal/packages.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package packagesinternal exposes internal-only fields from go/packages. +package packagesinternal + +import ( + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gocommand" +) + +var GetForTest = func(p interface{}) string { return "" } +var GetDepsErrors = func(p interface{}) []*PackageError { return nil } + +type PackageError struct { + ImportStack []string // shortest path from package named on command line to this one + Pos string // position of error (if present, file:line:col) + Err string // the error itself +} + +var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } + +var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} + +var TypecheckCgo int +var DepsErrors int // must be set as a LoadMode to call GetDepsErrors +var ForTest int // must be set as a LoadMode to call GetForTest + +var SetModFlag = func(config interface{}, value string) {} +var SetModFile = func(config interface{}, value string) {} diff --git a/gopls/golang_org_x_tools/pkgbits/codes.go b/gopls/golang_org_x_tools/pkgbits/codes.go new file mode 100644 index 0000000..f0cabde --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/codes.go @@ -0,0 +1,77 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +// A Code is an enum value that can be encoded into bitstreams. +// +// Code types are preferable for enum types, because they allow +// Decoder to detect desyncs. +type Code interface { + // Marker returns the SyncMarker for the Code's dynamic type. + Marker() SyncMarker + + // Value returns the Code's ordinal value. + Value() int +} + +// A CodeVal distinguishes among go/constant.Value encodings. +type CodeVal int + +func (c CodeVal) Marker() SyncMarker { return SyncVal } +func (c CodeVal) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + ValBool CodeVal = iota + ValString + ValInt64 + ValBigInt + ValBigRat + ValBigFloat +) + +// A CodeType distinguishes among go/types.Type encodings. +type CodeType int + +func (c CodeType) Marker() SyncMarker { return SyncType } +func (c CodeType) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + TypeBasic CodeType = iota + TypeNamed + TypePointer + TypeSlice + TypeArray + TypeChan + TypeMap + TypeSignature + TypeStruct + TypeInterface + TypeUnion + TypeTypeParam +) + +// A CodeObj distinguishes among go/types.Object encodings. +type CodeObj int + +func (c CodeObj) Marker() SyncMarker { return SyncCodeObj } +func (c CodeObj) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + ObjAlias CodeObj = iota + ObjConst + ObjType + ObjFunc + ObjVar + ObjStub +) diff --git a/gopls/golang_org_x_tools/pkgbits/decoder.go b/gopls/golang_org_x_tools/pkgbits/decoder.go new file mode 100644 index 0000000..e08099c --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/decoder.go @@ -0,0 +1,434 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import ( + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "io" + "math/big" + "os" + "runtime" + "strings" +) + +// A PkgDecoder provides methods for decoding a package's Unified IR +// export data. +type PkgDecoder struct { + // version is the file format version. + version uint32 + + // sync indicates whether the file uses sync markers. + sync bool + + // pkgPath is the package path for the package to be decoded. + // + // TODO(mdempsky): Remove; unneeded since CL 391014. + pkgPath string + + // elemData is the full data payload of the encoded package. + // Elements are densely and contiguously packed together. + // + // The last 8 bytes of elemData are the package fingerprint. + elemData string + + // elemEnds stores the byte-offset end positions of element + // bitstreams within elemData. + // + // For example, element I's bitstream data starts at elemEnds[I-1] + // (or 0, if I==0) and ends at elemEnds[I]. + // + // Note: elemEnds is indexed by absolute indices, not + // section-relative indices. + elemEnds []uint32 + + // elemEndsEnds stores the index-offset end positions of relocation + // sections within elemEnds. + // + // For example, section K's end positions start at elemEndsEnds[K-1] + // (or 0, if K==0) and end at elemEndsEnds[K]. + elemEndsEnds [numRelocs]uint32 +} + +// PkgPath returns the package path for the package +// +// TODO(mdempsky): Remove; unneeded since CL 391014. +func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath } + +// SyncMarkers reports whether pr uses sync markers. +func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } + +// NewPkgDecoder returns a PkgDecoder initialized to read the Unified +// IR export data from input. pkgPath is the package path for the +// compilation unit that produced the export data. +// +// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014. +func NewPkgDecoder(pkgPath, input string) PkgDecoder { + pr := PkgDecoder{ + pkgPath: pkgPath, + } + + // TODO(mdempsky): Implement direct indexing of input string to + // avoid copying the position information. + + r := strings.NewReader(input) + + assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil) + + switch pr.version { + default: + panic(fmt.Errorf("unsupported version: %v", pr.version)) + case 0: + // no flags + case 1: + var flags uint32 + assert(binary.Read(r, binary.LittleEndian, &flags) == nil) + pr.sync = flags&flagSyncMarkers != 0 + } + + assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) + + pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1]) + assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil) + + pos, err := r.Seek(0, io.SeekCurrent) + assert(err == nil) + + pr.elemData = input[pos:] + assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1])) + + return pr +} + +// NumElems returns the number of elements in section k. +func (pr *PkgDecoder) NumElems(k RelocKind) int { + count := int(pr.elemEndsEnds[k]) + if k > 0 { + count -= int(pr.elemEndsEnds[k-1]) + } + return count +} + +// TotalElems returns the total number of elements across all sections. +func (pr *PkgDecoder) TotalElems() int { + return len(pr.elemEnds) +} + +// Fingerprint returns the package fingerprint. +func (pr *PkgDecoder) Fingerprint() [8]byte { + var fp [8]byte + copy(fp[:], pr.elemData[len(pr.elemData)-8:]) + return fp +} + +// AbsIdx returns the absolute index for the given (section, index) +// pair. +func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int { + absIdx := int(idx) + if k > 0 { + absIdx += int(pr.elemEndsEnds[k-1]) + } + if absIdx >= int(pr.elemEndsEnds[k]) { + errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) + } + return absIdx +} + +// DataIdx returns the raw element bitstream for the given (section, +// index) pair. +func (pr *PkgDecoder) DataIdx(k RelocKind, idx Index) string { + absIdx := pr.AbsIdx(k, idx) + + var start uint32 + if absIdx > 0 { + start = pr.elemEnds[absIdx-1] + } + end := pr.elemEnds[absIdx] + + return pr.elemData[start:end] +} + +// StringIdx returns the string value for the given string index. +func (pr *PkgDecoder) StringIdx(idx Index) string { + return pr.DataIdx(RelocString, idx) +} + +// NewDecoder returns a Decoder for the given (section, index) pair, +// and decodes the given SyncMarker from the element bitstream. +func (pr *PkgDecoder) NewDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder { + r := pr.NewDecoderRaw(k, idx) + r.Sync(marker) + return r +} + +// NewDecoderRaw returns a Decoder for the given (section, index) pair. +// +// Most callers should use NewDecoder instead. +func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder { + r := Decoder{ + common: pr, + k: k, + Idx: idx, + } + + // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved. + r.Data = *strings.NewReader(pr.DataIdx(k, idx)) + + r.Sync(SyncRelocs) + r.Relocs = make([]RelocEnt, r.Len()) + for i := range r.Relocs { + r.Sync(SyncReloc) + r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())} + } + + return r +} + +// A Decoder provides methods for decoding an individual element's +// bitstream data. +type Decoder struct { + common *PkgDecoder + + Relocs []RelocEnt + Data strings.Reader + + k RelocKind + Idx Index +} + +func (r *Decoder) checkErr(err error) { + if err != nil { + errorf("unexpected decoding error: %w", err) + } +} + +func (r *Decoder) rawUvarint() uint64 { + x, err := binary.ReadUvarint(&r.Data) + r.checkErr(err) + return x +} + +func (r *Decoder) rawVarint() int64 { + ux := r.rawUvarint() + + // Zig-zag decode. + x := int64(ux >> 1) + if ux&1 != 0 { + x = ^x + } + return x +} + +func (r *Decoder) rawReloc(k RelocKind, idx int) Index { + e := r.Relocs[idx] + assert(e.Kind == k) + return e.Idx +} + +// Sync decodes a sync marker from the element bitstream and asserts +// that it matches the expected marker. +// +// If r.common.sync is false, then Sync is a no-op. +func (r *Decoder) Sync(mWant SyncMarker) { + if !r.common.sync { + return + } + + pos, _ := r.Data.Seek(0, io.SeekCurrent) + mHave := SyncMarker(r.rawUvarint()) + writerPCs := make([]int, r.rawUvarint()) + for i := range writerPCs { + writerPCs[i] = int(r.rawUvarint()) + } + + if mHave == mWant { + return + } + + // There's some tension here between printing: + // + // (1) full file paths that tools can recognize (e.g., so emacs + // hyperlinks the "file:line" text for easy navigation), or + // + // (2) short file paths that are easier for humans to read (e.g., by + // omitting redundant or irrelevant details, so it's easier to + // focus on the useful bits that remain). + // + // The current formatting favors the former, as it seems more + // helpful in practice. But perhaps the formatting could be improved + // to better address both concerns. For example, use relative file + // paths if they would be shorter, or rewrite file paths to contain + // "$GOROOT" (like objabi.AbsFile does) if tools can be taught how + // to reliably expand that again. + + fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.Idx, pos) + + fmt.Printf("\nfound %v, written at:\n", mHave) + if len(writerPCs) == 0 { + fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath) + } + for _, pc := range writerPCs { + fmt.Printf("\t%s\n", r.common.StringIdx(r.rawReloc(RelocString, pc))) + } + + fmt.Printf("\nexpected %v, reading at:\n", mWant) + var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size? + n := runtime.Callers(2, readerPCs[:]) + for _, pc := range fmtFrames(readerPCs[:n]...) { + fmt.Printf("\t%s\n", pc) + } + + // We already printed a stack trace for the reader, so now we can + // simply exit. Printing a second one with panic or base.Fatalf + // would just be noise. + os.Exit(1) +} + +// Bool decodes and returns a bool value from the element bitstream. +func (r *Decoder) Bool() bool { + r.Sync(SyncBool) + x, err := r.Data.ReadByte() + r.checkErr(err) + assert(x < 2) + return x != 0 +} + +// Int64 decodes and returns an int64 value from the element bitstream. +func (r *Decoder) Int64() int64 { + r.Sync(SyncInt64) + return r.rawVarint() +} + +// Int64 decodes and returns a uint64 value from the element bitstream. +func (r *Decoder) Uint64() uint64 { + r.Sync(SyncUint64) + return r.rawUvarint() +} + +// Len decodes and returns a non-negative int value from the element bitstream. +func (r *Decoder) Len() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v } + +// Int decodes and returns an int value from the element bitstream. +func (r *Decoder) Int() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v } + +// Uint decodes and returns a uint value from the element bitstream. +func (r *Decoder) Uint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v } + +// Code decodes a Code value from the element bitstream and returns +// its ordinal value. It's the caller's responsibility to convert the +// result to an appropriate Code type. +// +// TODO(mdempsky): Ideally this method would have signature "Code[T +// Code] T" instead, but we don't allow generic methods and the +// compiler can't depend on generics yet anyway. +func (r *Decoder) Code(mark SyncMarker) int { + r.Sync(mark) + return r.Len() +} + +// Reloc decodes a relocation of expected section k from the element +// bitstream and returns an index to the referenced element. +func (r *Decoder) Reloc(k RelocKind) Index { + r.Sync(SyncUseReloc) + return r.rawReloc(k, r.Len()) +} + +// String decodes and returns a string value from the element +// bitstream. +func (r *Decoder) String() string { + r.Sync(SyncString) + return r.common.StringIdx(r.Reloc(RelocString)) +} + +// Strings decodes and returns a variable-length slice of strings from +// the element bitstream. +func (r *Decoder) Strings() []string { + res := make([]string, r.Len()) + for i := range res { + res[i] = r.String() + } + return res +} + +// Value decodes and returns a constant.Value from the element +// bitstream. +func (r *Decoder) Value() constant.Value { + r.Sync(SyncValue) + isComplex := r.Bool() + val := r.scalar() + if isComplex { + val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar())) + } + return val +} + +func (r *Decoder) scalar() constant.Value { + switch tag := CodeVal(r.Code(SyncVal)); tag { + default: + panic(fmt.Errorf("unexpected scalar tag: %v", tag)) + + case ValBool: + return constant.MakeBool(r.Bool()) + case ValString: + return constant.MakeString(r.String()) + case ValInt64: + return constant.MakeInt64(r.Int64()) + case ValBigInt: + return constant.Make(r.bigInt()) + case ValBigRat: + num := r.bigInt() + denom := r.bigInt() + return constant.Make(new(big.Rat).SetFrac(num, denom)) + case ValBigFloat: + return constant.Make(r.bigFloat()) + } +} + +func (r *Decoder) bigInt() *big.Int { + v := new(big.Int).SetBytes([]byte(r.String())) + if r.Bool() { + v.Neg(v) + } + return v +} + +func (r *Decoder) bigFloat() *big.Float { + v := new(big.Float).SetPrec(512) + assert(v.UnmarshalText([]byte(r.String())) == nil) + return v +} + +// @@@ Helpers + +// TODO(mdempsky): These should probably be removed. I think they're a +// smell that the export data format is not yet quite right. + +// PeekPkgPath returns the package path for the specified package +// index. +func (pr *PkgDecoder) PeekPkgPath(idx Index) string { + r := pr.NewDecoder(RelocPkg, idx, SyncPkgDef) + path := r.String() + if path == "" { + path = pr.pkgPath + } + return path +} + +// PeekObj returns the package path, object name, and CodeObj for the +// specified object index. +func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) { + r := pr.NewDecoder(RelocName, idx, SyncObject1) + r.Sync(SyncSym) + r.Sync(SyncPkg) + path := pr.PeekPkgPath(r.Reloc(RelocPkg)) + name := r.String() + assert(name != "") + + tag := CodeObj(r.Code(SyncCodeObj)) + + return path, name, tag +} diff --git a/gopls/golang_org_x_tools/pkgbits/doc.go b/gopls/golang_org_x_tools/pkgbits/doc.go new file mode 100644 index 0000000..c8a2796 --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/doc.go @@ -0,0 +1,32 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkgbits implements low-level coding abstractions for +// Unified IR's export data format. +// +// At a low-level, a package is a collection of bitstream elements. +// Each element has a "kind" and a dense, non-negative index. +// Elements can be randomly accessed given their kind and index. +// +// Individual elements are sequences of variable-length values (e.g., +// integers, booleans, strings, go/constant values, cross-references +// to other elements). Package pkgbits provides APIs for encoding and +// decoding these low-level values, but the details of mapping +// higher-level Go constructs into elements is left to higher-level +// abstractions. +// +// Elements may cross-reference each other with "relocations." For +// example, an element representing a pointer type has a relocation +// referring to the element type. +// +// Go constructs may be composed as a constellation of multiple +// elements. For example, a declared function may have one element to +// describe the object (e.g., its name, type, position), and a +// separate element to describe its function body. This allows readers +// some flexibility in efficiently seeking or re-reading data (e.g., +// inlining requires re-reading the function body for each inlined +// call, without needing to re-read the object-level details). +// +// This is a copy of internal/pkgbits in the Go implementation. +package pkgbits diff --git a/gopls/golang_org_x_tools/pkgbits/encoder.go b/gopls/golang_org_x_tools/pkgbits/encoder.go new file mode 100644 index 0000000..e98e411 --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/encoder.go @@ -0,0 +1,383 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import ( + "bytes" + "crypto/md5" + "encoding/binary" + "go/constant" + "io" + "math/big" + "runtime" +) + +// currentVersion is the current version number. +// +// - v0: initial prototype +// +// - v1: adds the flags uint32 word +const currentVersion uint32 = 1 + +// A PkgEncoder provides methods for encoding a package's Unified IR +// export data. +type PkgEncoder struct { + // elems holds the bitstream for previously encoded elements. + elems [numRelocs][]string + + // stringsIdx maps previously encoded strings to their index within + // the RelocString section, to allow deduplication. That is, + // elems[RelocString][stringsIdx[s]] == s (if present). + stringsIdx map[string]Index + + // syncFrames is the number of frames to write at each sync + // marker. A negative value means sync markers are omitted. + syncFrames int +} + +// SyncMarkers reports whether pw uses sync markers. +func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 } + +// NewPkgEncoder returns an initialized PkgEncoder. +// +// syncFrames is the number of caller frames that should be serialized +// at Sync points. Serializing additional frames results in larger +// export data files, but can help diagnosing desync errors in +// higher-level Unified IR reader/writer code. If syncFrames is +// negative, then sync markers are omitted entirely. +func NewPkgEncoder(syncFrames int) PkgEncoder { + return PkgEncoder{ + stringsIdx: make(map[string]Index), + syncFrames: syncFrames, + } +} + +// DumpTo writes the package's encoded data to out0 and returns the +// package fingerprint. +func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { + h := md5.New() + out := io.MultiWriter(out0, h) + + writeUint32 := func(x uint32) { + assert(binary.Write(out, binary.LittleEndian, x) == nil) + } + + writeUint32(currentVersion) + + var flags uint32 + if pw.SyncMarkers() { + flags |= flagSyncMarkers + } + writeUint32(flags) + + // Write elemEndsEnds. + var sum uint32 + for _, elems := range &pw.elems { + sum += uint32(len(elems)) + writeUint32(sum) + } + + // Write elemEnds. + sum = 0 + for _, elems := range &pw.elems { + for _, elem := range elems { + sum += uint32(len(elem)) + writeUint32(sum) + } + } + + // Write elemData. + for _, elems := range &pw.elems { + for _, elem := range elems { + _, err := io.WriteString(out, elem) + assert(err == nil) + } + } + + // Write fingerprint. + copy(fingerprint[:], h.Sum(nil)) + _, err := out0.Write(fingerprint[:]) + assert(err == nil) + + return +} + +// StringIdx adds a string value to the strings section, if not +// already present, and returns its index. +func (pw *PkgEncoder) StringIdx(s string) Index { + if idx, ok := pw.stringsIdx[s]; ok { + assert(pw.elems[RelocString][idx] == s) + return idx + } + + idx := Index(len(pw.elems[RelocString])) + pw.elems[RelocString] = append(pw.elems[RelocString], s) + pw.stringsIdx[s] = idx + return idx +} + +// NewEncoder returns an Encoder for a new element within the given +// section, and encodes the given SyncMarker as the start of the +// element bitstream. +func (pw *PkgEncoder) NewEncoder(k RelocKind, marker SyncMarker) Encoder { + e := pw.NewEncoderRaw(k) + e.Sync(marker) + return e +} + +// NewEncoderRaw returns an Encoder for a new element within the given +// section. +// +// Most callers should use NewEncoder instead. +func (pw *PkgEncoder) NewEncoderRaw(k RelocKind) Encoder { + idx := Index(len(pw.elems[k])) + pw.elems[k] = append(pw.elems[k], "") // placeholder + + return Encoder{ + p: pw, + k: k, + Idx: idx, + } +} + +// An Encoder provides methods for encoding an individual element's +// bitstream data. +type Encoder struct { + p *PkgEncoder + + Relocs []RelocEnt + RelocMap map[RelocEnt]uint32 + Data bytes.Buffer // accumulated element bitstream data + + encodingRelocHeader bool + + k RelocKind + Idx Index // index within relocation section +} + +// Flush finalizes the element's bitstream and returns its Index. +func (w *Encoder) Flush() Index { + var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved + + // Backup the data so we write the relocations at the front. + var tmp bytes.Buffer + io.Copy(&tmp, &w.Data) + + // TODO(mdempsky): Consider writing these out separately so they're + // easier to strip, along with function bodies, so that we can prune + // down to just the data that's relevant to go/types. + if w.encodingRelocHeader { + panic("encodingRelocHeader already true; recursive flush?") + } + w.encodingRelocHeader = true + w.Sync(SyncRelocs) + w.Len(len(w.Relocs)) + for _, rEnt := range w.Relocs { + w.Sync(SyncReloc) + w.Len(int(rEnt.Kind)) + w.Len(int(rEnt.Idx)) + } + + io.Copy(&sb, &w.Data) + io.Copy(&sb, &tmp) + w.p.elems[w.k][w.Idx] = sb.String() + + return w.Idx +} + +func (w *Encoder) checkErr(err error) { + if err != nil { + errorf("unexpected encoding error: %v", err) + } +} + +func (w *Encoder) rawUvarint(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + _, err := w.Data.Write(buf[:n]) + w.checkErr(err) +} + +func (w *Encoder) rawVarint(x int64) { + // Zig-zag encode. + ux := uint64(x) << 1 + if x < 0 { + ux = ^ux + } + + w.rawUvarint(ux) +} + +func (w *Encoder) rawReloc(r RelocKind, idx Index) int { + e := RelocEnt{r, idx} + if w.RelocMap != nil { + if i, ok := w.RelocMap[e]; ok { + return int(i) + } + } else { + w.RelocMap = make(map[RelocEnt]uint32) + } + + i := len(w.Relocs) + w.RelocMap[e] = uint32(i) + w.Relocs = append(w.Relocs, e) + return i +} + +func (w *Encoder) Sync(m SyncMarker) { + if !w.p.SyncMarkers() { + return + } + + // Writing out stack frame string references requires working + // relocations, but writing out the relocations themselves involves + // sync markers. To prevent infinite recursion, we simply trim the + // stack frame for sync markers within the relocation header. + var frames []string + if !w.encodingRelocHeader && w.p.syncFrames > 0 { + pcs := make([]uintptr, w.p.syncFrames) + n := runtime.Callers(2, pcs) + frames = fmtFrames(pcs[:n]...) + } + + // TODO(mdempsky): Save space by writing out stack frames as a + // linked list so we can share common stack frames. + w.rawUvarint(uint64(m)) + w.rawUvarint(uint64(len(frames))) + for _, frame := range frames { + w.rawUvarint(uint64(w.rawReloc(RelocString, w.p.StringIdx(frame)))) + } +} + +// Bool encodes and writes a bool value into the element bitstream, +// and then returns the bool value. +// +// For simple, 2-alternative encodings, the idiomatic way to call Bool +// is something like: +// +// if w.Bool(x != 0) { +// // alternative #1 +// } else { +// // alternative #2 +// } +// +// For multi-alternative encodings, use Code instead. +func (w *Encoder) Bool(b bool) bool { + w.Sync(SyncBool) + var x byte + if b { + x = 1 + } + err := w.Data.WriteByte(x) + w.checkErr(err) + return b +} + +// Int64 encodes and writes an int64 value into the element bitstream. +func (w *Encoder) Int64(x int64) { + w.Sync(SyncInt64) + w.rawVarint(x) +} + +// Uint64 encodes and writes a uint64 value into the element bitstream. +func (w *Encoder) Uint64(x uint64) { + w.Sync(SyncUint64) + w.rawUvarint(x) +} + +// Len encodes and writes a non-negative int value into the element bitstream. +func (w *Encoder) Len(x int) { assert(x >= 0); w.Uint64(uint64(x)) } + +// Int encodes and writes an int value into the element bitstream. +func (w *Encoder) Int(x int) { w.Int64(int64(x)) } + +// Len encodes and writes a uint value into the element bitstream. +func (w *Encoder) Uint(x uint) { w.Uint64(uint64(x)) } + +// Reloc encodes and writes a relocation for the given (section, +// index) pair into the element bitstream. +// +// Note: Only the index is formally written into the element +// bitstream, so bitstream decoders must know from context which +// section an encoded relocation refers to. +func (w *Encoder) Reloc(r RelocKind, idx Index) { + w.Sync(SyncUseReloc) + w.Len(w.rawReloc(r, idx)) +} + +// Code encodes and writes a Code value into the element bitstream. +func (w *Encoder) Code(c Code) { + w.Sync(c.Marker()) + w.Len(c.Value()) +} + +// String encodes and writes a string value into the element +// bitstream. +// +// Internally, strings are deduplicated by adding them to the strings +// section (if not already present), and then writing a relocation +// into the element bitstream. +func (w *Encoder) String(s string) { + w.Sync(SyncString) + w.Reloc(RelocString, w.p.StringIdx(s)) +} + +// Strings encodes and writes a variable-length slice of strings into +// the element bitstream. +func (w *Encoder) Strings(ss []string) { + w.Len(len(ss)) + for _, s := range ss { + w.String(s) + } +} + +// Value encodes and writes a constant.Value into the element +// bitstream. +func (w *Encoder) Value(val constant.Value) { + w.Sync(SyncValue) + if w.Bool(val.Kind() == constant.Complex) { + w.scalar(constant.Real(val)) + w.scalar(constant.Imag(val)) + } else { + w.scalar(val) + } +} + +func (w *Encoder) scalar(val constant.Value) { + switch v := constant.Val(val).(type) { + default: + errorf("unhandled %v (%v)", val, val.Kind()) + case bool: + w.Code(ValBool) + w.Bool(v) + case string: + w.Code(ValString) + w.String(v) + case int64: + w.Code(ValInt64) + w.Int64(v) + case *big.Int: + w.Code(ValBigInt) + w.bigInt(v) + case *big.Rat: + w.Code(ValBigRat) + w.bigInt(v.Num()) + w.bigInt(v.Denom()) + case *big.Float: + w.Code(ValBigFloat) + w.bigFloat(v) + } +} + +func (w *Encoder) bigInt(v *big.Int) { + b := v.Bytes() + w.String(string(b)) // TODO: More efficient encoding. + w.Bool(v.Sign() < 0) +} + +func (w *Encoder) bigFloat(v *big.Float) { + b := v.Append(nil, 'p', -1) + w.String(string(b)) // TODO: More efficient encoding. +} diff --git a/gopls/golang_org_x_tools/pkgbits/flags.go b/gopls/golang_org_x_tools/pkgbits/flags.go new file mode 100644 index 0000000..6542227 --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/flags.go @@ -0,0 +1,9 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +const ( + flagSyncMarkers = 1 << iota // file format contains sync markers +) diff --git a/gopls/golang_org_x_tools/pkgbits/frames_go1.go b/gopls/golang_org_x_tools/pkgbits/frames_go1.go new file mode 100644 index 0000000..5294f6a --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/frames_go1.go @@ -0,0 +1,21 @@ +// 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.7 +// +build !go1.7 + +// TODO(mdempsky): Remove after #44505 is resolved + +package pkgbits + +import "runtime" + +func walkFrames(pcs []uintptr, visit frameVisitor) { + for _, pc := range pcs { + fn := runtime.FuncForPC(pc) + file, line := fn.FileLine(pc) + + visit(file, line, fn.Name(), pc-fn.Entry()) + } +} diff --git a/gopls/golang_org_x_tools/pkgbits/frames_go17.go b/gopls/golang_org_x_tools/pkgbits/frames_go17.go new file mode 100644 index 0000000..2324ae7 --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/frames_go17.go @@ -0,0 +1,28 @@ +// 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.7 +// +build go1.7 + +package pkgbits + +import "runtime" + +// walkFrames calls visit for each call frame represented by pcs. +// +// pcs should be a slice of PCs, as returned by runtime.Callers. +func walkFrames(pcs []uintptr, visit frameVisitor) { + if len(pcs) == 0 { + return + } + + frames := runtime.CallersFrames(pcs) + for { + frame, more := frames.Next() + visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) + if !more { + return + } + } +} diff --git a/gopls/golang_org_x_tools/pkgbits/reloc.go b/gopls/golang_org_x_tools/pkgbits/reloc.go new file mode 100644 index 0000000..fcdfb97 --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/reloc.go @@ -0,0 +1,42 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +// A RelocKind indicates a particular section within a unified IR export. +type RelocKind int32 + +// An Index represents a bitstream element index within a particular +// section. +type Index int32 + +// A relocEnt (relocation entry) is an entry in an element's local +// reference table. +// +// TODO(mdempsky): Rename this too. +type RelocEnt struct { + Kind RelocKind + Idx Index +} + +// Reserved indices within the meta relocation section. +const ( + PublicRootIdx Index = 0 + PrivateRootIdx Index = 1 +) + +const ( + RelocString RelocKind = iota + RelocMeta + RelocPosBase + RelocPkg + RelocName + RelocType + RelocObj + RelocObjExt + RelocObjDict + RelocBody + + numRelocs = iota +) diff --git a/gopls/golang_org_x_tools/pkgbits/support.go b/gopls/golang_org_x_tools/pkgbits/support.go new file mode 100644 index 0000000..ad26d3b --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/support.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import "fmt" + +func assert(b bool) { + if !b { + panic("assertion failed") + } +} + +func errorf(format string, args ...interface{}) { + panic(fmt.Errorf(format, args...)) +} diff --git a/gopls/golang_org_x_tools/pkgbits/sync.go b/gopls/golang_org_x_tools/pkgbits/sync.go new file mode 100644 index 0000000..5bd51ef --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/sync.go @@ -0,0 +1,113 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import ( + "fmt" + "strings" +) + +// fmtFrames formats a backtrace for reporting reader/writer desyncs. +func fmtFrames(pcs ...uintptr) []string { + res := make([]string, 0, len(pcs)) + walkFrames(pcs, func(file string, line int, name string, offset uintptr) { + // Trim package from function name. It's just redundant noise. + name = strings.TrimPrefix(name, "cmd/compile/internal/noder.") + + res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset)) + }) + return res +} + +type frameVisitor func(file string, line int, name string, offset uintptr) + +// SyncMarker is an enum type that represents markers that may be +// written to export data to ensure the reader and writer stay +// synchronized. +type SyncMarker int + +//go:generate stringer -type=SyncMarker -trimprefix=Sync + +const ( + _ SyncMarker = iota + + // Public markers (known to go/types importers). + + // Low-level coding markers. + SyncEOF + SyncBool + SyncInt64 + SyncUint64 + SyncString + SyncValue + SyncVal + SyncRelocs + SyncReloc + SyncUseReloc + + // Higher-level object and type markers. + SyncPublic + SyncPos + SyncPosBase + SyncObject + SyncObject1 + SyncPkg + SyncPkgDef + SyncMethod + SyncType + SyncTypeIdx + SyncTypeParamNames + SyncSignature + SyncParams + SyncParam + SyncCodeObj + SyncSym + SyncLocalIdent + SyncSelector + + // Private markers (only known to cmd/compile). + SyncPrivate + + SyncFuncExt + SyncVarExt + SyncTypeExt + SyncPragma + + SyncExprList + SyncExprs + SyncExpr + SyncExprType + SyncAssign + SyncOp + SyncFuncLit + SyncCompLit + + SyncDecl + SyncFuncBody + SyncOpenScope + SyncCloseScope + SyncCloseAnotherScope + SyncDeclNames + SyncDeclName + + SyncStmts + SyncBlockStmt + SyncIfStmt + SyncForStmt + SyncSwitchStmt + SyncRangeStmt + SyncCaseClause + SyncCommClause + SyncSelectStmt + SyncDecls + SyncLabeledStmt + SyncUseObjLocal + SyncAddLocal + SyncLinkname + SyncStmt1 + SyncStmtsEnd + SyncLabel + SyncOptLabel +) diff --git a/gopls/golang_org_x_tools/pkgbits/syncmarker_string.go b/gopls/golang_org_x_tools/pkgbits/syncmarker_string.go new file mode 100644 index 0000000..4a5b0ca --- /dev/null +++ b/gopls/golang_org_x_tools/pkgbits/syncmarker_string.go @@ -0,0 +1,89 @@ +// Code generated by "stringer -type=SyncMarker -trimprefix=Sync"; DO NOT EDIT. + +package pkgbits + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[SyncEOF-1] + _ = x[SyncBool-2] + _ = x[SyncInt64-3] + _ = x[SyncUint64-4] + _ = x[SyncString-5] + _ = x[SyncValue-6] + _ = x[SyncVal-7] + _ = x[SyncRelocs-8] + _ = x[SyncReloc-9] + _ = x[SyncUseReloc-10] + _ = x[SyncPublic-11] + _ = x[SyncPos-12] + _ = x[SyncPosBase-13] + _ = x[SyncObject-14] + _ = x[SyncObject1-15] + _ = x[SyncPkg-16] + _ = x[SyncPkgDef-17] + _ = x[SyncMethod-18] + _ = x[SyncType-19] + _ = x[SyncTypeIdx-20] + _ = x[SyncTypeParamNames-21] + _ = x[SyncSignature-22] + _ = x[SyncParams-23] + _ = x[SyncParam-24] + _ = x[SyncCodeObj-25] + _ = x[SyncSym-26] + _ = x[SyncLocalIdent-27] + _ = x[SyncSelector-28] + _ = x[SyncPrivate-29] + _ = x[SyncFuncExt-30] + _ = x[SyncVarExt-31] + _ = x[SyncTypeExt-32] + _ = x[SyncPragma-33] + _ = x[SyncExprList-34] + _ = x[SyncExprs-35] + _ = x[SyncExpr-36] + _ = x[SyncExprType-37] + _ = x[SyncAssign-38] + _ = x[SyncOp-39] + _ = x[SyncFuncLit-40] + _ = x[SyncCompLit-41] + _ = x[SyncDecl-42] + _ = x[SyncFuncBody-43] + _ = x[SyncOpenScope-44] + _ = x[SyncCloseScope-45] + _ = x[SyncCloseAnotherScope-46] + _ = x[SyncDeclNames-47] + _ = x[SyncDeclName-48] + _ = x[SyncStmts-49] + _ = x[SyncBlockStmt-50] + _ = x[SyncIfStmt-51] + _ = x[SyncForStmt-52] + _ = x[SyncSwitchStmt-53] + _ = x[SyncRangeStmt-54] + _ = x[SyncCaseClause-55] + _ = x[SyncCommClause-56] + _ = x[SyncSelectStmt-57] + _ = x[SyncDecls-58] + _ = x[SyncLabeledStmt-59] + _ = x[SyncUseObjLocal-60] + _ = x[SyncAddLocal-61] + _ = x[SyncLinkname-62] + _ = x[SyncStmt1-63] + _ = x[SyncStmtsEnd-64] + _ = x[SyncLabel-65] + _ = x[SyncOptLabel-66] +} + +const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel" + +var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458} + +func (i SyncMarker) String() string { + i -= 1 + if i < 0 || i >= SyncMarker(len(_SyncMarker_index)-1) { + return "SyncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _SyncMarker_name[_SyncMarker_index[i]:_SyncMarker_index[i+1]] +} diff --git a/gopls/golang_org_x_tools/typeparams/common.go b/gopls/golang_org_x_tools/typeparams/common.go new file mode 100644 index 0000000..25a1426 --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/common.go @@ -0,0 +1,179 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package typeparams contains common utilities for writing tools that interact +// with generic Go code, as introduced with Go 1.18. +// +// Many of the types and functions in this package are proxies for the new APIs +// introduced in the standard library with Go 1.18. For example, the +// typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec +// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go +// versions older than 1.18 these helpers are implemented as stubs, allowing +// users of this package to write code that handles generic constructs inline, +// even if the Go version being used to compile does not support generics. +// +// Additionally, this package contains common utilities for working with the +// new generic constructs, to supplement the standard library APIs. Notably, +// the StructuralTerms API computes a minimal representation of the structural +// restrictions on a type parameter. +// +// An external version of these APIs is available in the +// golang.org/x/exp/typeparams module. +package typeparams + +import ( + "go/ast" + "go/token" + "go/types" +) + +// UnpackIndexExpr extracts data from AST nodes that represent index +// expressions. +// +// For an ast.IndexExpr, the resulting indices slice will contain exactly one +// index expression. For an ast.IndexListExpr (go1.18+), it may have a variable +// number of index expressions. +// +// For nodes that don't represent index expressions, the first return value of +// UnpackIndexExpr will be nil. +func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) { + switch e := n.(type) { + case *ast.IndexExpr: + return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack + case *IndexListExpr: + return e.X, e.Lbrack, e.Indices, e.Rbrack + } + return nil, token.NoPos, nil, token.NoPos +} + +// PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on +// the cardinality of indices. Calling PackIndexExpr with len(indices) == 0 +// will panic. +func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr { + switch len(indices) { + case 0: + panic("empty indices") + case 1: + return &ast.IndexExpr{ + X: x, + Lbrack: lbrack, + Index: indices[0], + Rbrack: rbrack, + } + default: + return &IndexListExpr{ + X: x, + Lbrack: lbrack, + Indices: indices, + Rbrack: rbrack, + } + } +} + +// IsTypeParam reports whether t is a type parameter. +func IsTypeParam(t types.Type) bool { + _, ok := t.(*TypeParam) + return ok +} + +// OriginMethod returns the origin method associated with the method fn. +// For methods on a non-generic receiver base type, this is just +// fn. However, for methods with a generic receiver, OriginMethod returns the +// corresponding method in the method set of the origin type. +// +// As a special case, if fn is not a method (has no receiver), OriginMethod +// returns fn. +func OriginMethod(fn *types.Func) *types.Func { + recv := fn.Type().(*types.Signature).Recv() + if recv == nil { + + return fn + } + base := recv.Type() + p, isPtr := base.(*types.Pointer) + if isPtr { + base = p.Elem() + } + named, isNamed := base.(*types.Named) + if !isNamed { + // Receiver is a *types.Interface. + return fn + } + if ForNamed(named).Len() == 0 { + // Receiver base has no type parameters, so we can avoid the lookup below. + return fn + } + orig := NamedTypeOrigin(named) + gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) + return gfn.(*types.Func) +} + +// GenericAssignableTo is a generalization of types.AssignableTo that +// implements the following rule for uninstantiated generic types: +// +// If V and T are generic named types, then V is considered assignable to T if, +// for every possible instantation of V[A_1, ..., A_N], the instantiation +// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N]. +// +// If T has structural constraints, they must be satisfied by V. +// +// For example, consider the following type declarations: +// +// type Interface[T any] interface { +// Accept(T) +// } +// +// type Container[T any] struct { +// Element T +// } +// +// func (c Container[T]) Accept(t T) { c.Element = t } +// +// In this case, GenericAssignableTo reports that instantiations of Container +// are assignable to the corresponding instantiation of Interface. +func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { + // If V and T are not both named, or do not have matching non-empty type + // parameter lists, fall back on types.AssignableTo. + + VN, Vnamed := V.(*types.Named) + TN, Tnamed := T.(*types.Named) + if !Vnamed || !Tnamed { + return types.AssignableTo(V, T) + } + + vtparams := ForNamed(VN) + ttparams := ForNamed(TN) + if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 { + return types.AssignableTo(V, T) + } + + // V and T have the same (non-zero) number of type params. Instantiate both + // with the type parameters of V. This must always succeed for V, and will + // succeed for T if and only if the type set of each type parameter of V is a + // subset of the type set of the corresponding type parameter of T, meaning + // that every instantiation of V corresponds to a valid instantiation of T. + + // Minor optimization: ensure we share a context across the two + // instantiations below. + if ctxt == nil { + ctxt = NewContext() + } + + var targs []types.Type + for i := 0; i < vtparams.Len(); i++ { + targs = append(targs, vtparams.At(i)) + } + + vinst, err := Instantiate(ctxt, V, targs, true) + if err != nil { + panic("type parameters should satisfy their own constraints") + } + + tinst, err := Instantiate(ctxt, T, targs, true) + if err != nil { + return false + } + + return types.AssignableTo(vinst, tinst) +} diff --git a/gopls/golang_org_x_tools/typeparams/coretype.go b/gopls/golang_org_x_tools/typeparams/coretype.go new file mode 100644 index 0000000..993135e --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/coretype.go @@ -0,0 +1,122 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeparams + +import ( + "go/types" +) + +// CoreType returns the core type of T or nil if T does not have a core type. +// +// See https://go.dev/ref/spec#Core_types for the definition of a core type. +func CoreType(T types.Type) types.Type { + U := T.Underlying() + if _, ok := U.(*types.Interface); !ok { + return U // for non-interface types, + } + + terms, err := _NormalTerms(U) + if len(terms) == 0 || err != nil { + // len(terms) -> empty type set of interface. + // err != nil => U is invalid, exceeds complexity bounds, or has an empty type set. + return nil // no core type. + } + + U = terms[0].Type().Underlying() + var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying()) + for identical = 1; identical < len(terms); identical++ { + if !types.Identical(U, terms[identical].Type().Underlying()) { + break + } + } + + if identical == len(terms) { + // https://go.dev/ref/spec#Core_types + // "There is a single type U which is the underlying type of all types in the type set of T" + return U + } + ch, ok := U.(*types.Chan) + if !ok { + return nil // no core type as identical < len(terms) and U is not a channel. + } + // https://go.dev/ref/spec#Core_types + // "the type chan E if T contains only bidirectional channels, or the type chan<- E or + // <-chan E depending on the direction of the directional channels present." + for chans := identical; chans < len(terms); chans++ { + curr, ok := terms[chans].Type().Underlying().(*types.Chan) + if !ok { + return nil + } + if !types.Identical(ch.Elem(), curr.Elem()) { + return nil // channel elements are not identical. + } + if ch.Dir() == types.SendRecv { + // ch is bidirectional. We can safely always use curr's direction. + ch = curr + } else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() { + // ch and curr are not bidirectional and not the same direction. + return nil + } + } + return ch +} + +// _NormalTerms returns a slice of terms representing the normalized structural +// type restrictions of a type, if any. +// +// For all types other than *types.TypeParam, *types.Interface, and +// *types.Union, this is just a single term with Tilde() == false and +// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see +// below. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration type +// T[P interface{~int; m()}] int the structural restriction of the type +// parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// _NormalTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, _NormalTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the type is +// invalid, exceeds complexity bounds, or has an empty type set. In the latter +// case, _NormalTerms returns ErrEmptyTypeSet. +// +// _NormalTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func _NormalTerms(typ types.Type) ([]*Term, error) { + switch typ := typ.(type) { + case *TypeParam: + return StructuralTerms(typ) + case *Union: + return UnionTermSet(typ) + case *types.Interface: + return InterfaceTermSet(typ) + default: + return []*Term{NewTerm(false, typ)}, nil + } +} diff --git a/gopls/golang_org_x_tools/typeparams/enabled_go117.go b/gopls/golang_org_x_tools/typeparams/enabled_go117.go new file mode 100644 index 0000000..1821239 --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/enabled_go117.go @@ -0,0 +1,12 @@ +// 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 typeparams + +// Enabled reports whether type parameters are enabled in the current build +// environment. +const Enabled = false diff --git a/gopls/golang_org_x_tools/typeparams/enabled_go118.go b/gopls/golang_org_x_tools/typeparams/enabled_go118.go new file mode 100644 index 0000000..d671488 --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/enabled_go118.go @@ -0,0 +1,15 @@ +// 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 typeparams + +// Note: this constant is in a separate file as this is the only acceptable +// diff between the <1.18 API of this package and the 1.18 API. + +// Enabled reports whether type parameters are enabled in the current build +// environment. +const Enabled = true diff --git a/gopls/golang_org_x_tools/typeparams/normalize.go b/gopls/golang_org_x_tools/typeparams/normalize.go new file mode 100644 index 0000000..9c631b6 --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/normalize.go @@ -0,0 +1,218 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeparams + +import ( + "errors" + "fmt" + "go/types" + "os" + "strings" +) + +//go:generate go run copytermlist.go + +const debug = false + +var ErrEmptyTypeSet = errors.New("empty type set") + +// StructuralTerms returns a slice of terms representing the normalized +// structural type restrictions of a type parameter, if any. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration +// +// type T[P interface{~int; m()}] int +// +// the structural restriction of the type parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// StructuralTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, StructuralTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the +// constraint interface is invalid, exceeds complexity bounds, or has an empty +// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet. +// +// StructuralTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func StructuralTerms(tparam *TypeParam) ([]*Term, error) { + constraint := tparam.Constraint() + if constraint == nil { + return nil, fmt.Errorf("%s has nil constraint", tparam) + } + iface, _ := constraint.Underlying().(*types.Interface) + if iface == nil { + return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying()) + } + return InterfaceTermSet(iface) +} + +// InterfaceTermSet computes the normalized terms for a constraint interface, +// returning an error if the term set cannot be computed or is empty. In the +// latter case, the error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func InterfaceTermSet(iface *types.Interface) ([]*Term, error) { + return computeTermSet(iface) +} + +// UnionTermSet computes the normalized terms for a union, returning an error +// if the term set cannot be computed or is empty. In the latter case, the +// error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func UnionTermSet(union *Union) ([]*Term, error) { + return computeTermSet(union) +} + +func computeTermSet(typ types.Type) ([]*Term, error) { + tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) + if err != nil { + return nil, err + } + if tset.terms.isEmpty() { + return nil, ErrEmptyTypeSet + } + if tset.terms.isAll() { + return nil, nil + } + var terms []*Term + for _, term := range tset.terms { + terms = append(terms, NewTerm(term.tilde, term.typ)) + } + return terms, nil +} + +// A termSet holds the normalized set of terms for a given type. +// +// The name termSet is intentionally distinct from 'type set': a type set is +// all types that implement a type (and includes method restrictions), whereas +// a term set just represents the structural restrictions on a type. +type termSet struct { + complete bool + terms termlist +} + +func indentf(depth int, format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...) +} + +func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) { + if t == nil { + panic("nil type") + } + + if debug { + indentf(depth, "%s", t.String()) + defer func() { + if err != nil { + indentf(depth, "=> %s", err) + } else { + indentf(depth, "=> %s", res.terms.String()) + } + }() + } + + const maxTermCount = 100 + if tset, ok := seen[t]; ok { + if !tset.complete { + return nil, fmt.Errorf("cycle detected in the declaration of %s", t) + } + return tset, nil + } + + // Mark the current type as seen to avoid infinite recursion. + tset := new(termSet) + defer func() { + tset.complete = true + }() + seen[t] = tset + + switch u := t.Underlying().(type) { + case *types.Interface: + // The term set of an interface is the intersection of the term sets of its + // embedded types. + tset.terms = allTermlist + for i := 0; i < u.NumEmbeddeds(); i++ { + embedded := u.EmbeddedType(i) + if _, ok := embedded.Underlying().(*TypeParam); ok { + return nil, fmt.Errorf("invalid embedded type %T", embedded) + } + tset2, err := computeTermSetInternal(embedded, seen, depth+1) + if err != nil { + return nil, err + } + tset.terms = tset.terms.intersect(tset2.terms) + } + case *Union: + // The term set of a union is the union of term sets of its terms. + tset.terms = nil + for i := 0; i < u.Len(); i++ { + t := u.Term(i) + var terms termlist + switch t.Type().Underlying().(type) { + case *types.Interface: + tset2, err := computeTermSetInternal(t.Type(), seen, depth+1) + if err != nil { + return nil, err + } + terms = tset2.terms + case *TypeParam, *Union: + // A stand-alone type parameter or union is not permitted as union + // term. + return nil, fmt.Errorf("invalid union term %T", t) + default: + if t.Type() == types.Typ[types.Invalid] { + continue + } + terms = termlist{{t.Tilde(), t.Type()}} + } + tset.terms = tset.terms.union(terms) + if len(tset.terms) > maxTermCount { + return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) + } + } + case *TypeParam: + panic("unreachable") + default: + // For all other types, the term set is just a single non-tilde term + // holding the type itself. + if u != types.Typ[types.Invalid] { + tset.terms = termlist{{false, t}} + } + } + return tset, nil +} + +// under is a facade for the go/types internal function of the same name. It is +// used by typeterm.go. +func under(t types.Type) types.Type { + return t.Underlying() +} diff --git a/gopls/golang_org_x_tools/typeparams/termlist.go b/gopls/golang_org_x_tools/typeparams/termlist.go new file mode 100644 index 0000000..933106a --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/termlist.go @@ -0,0 +1,163 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by copytermlist.go DO NOT EDIT. + +package typeparams + +import ( + "bytes" + "go/types" +) + +// A termlist represents the type set represented by the union +// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn. +// A termlist is in normal form if all terms are disjoint. +// termlist operations don't require the operands to be in +// normal form. +type termlist []*term + +// allTermlist represents the set of all types. +// It is in normal form. +var allTermlist = termlist{new(term)} + +// String prints the termlist exactly (without normalization). +func (xl termlist) String() string { + if len(xl) == 0 { + return "∅" + } + var buf bytes.Buffer + for i, x := range xl { + if i > 0 { + buf.WriteString(" ∪ ") + } + buf.WriteString(x.String()) + } + return buf.String() +} + +// isEmpty reports whether the termlist xl represents the empty set of types. +func (xl termlist) isEmpty() bool { + // If there's a non-nil term, the entire list is not empty. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil { + return false + } + } + return true +} + +// isAll reports whether the termlist xl represents the set of all types. +func (xl termlist) isAll() bool { + // If there's a 𝓤 term, the entire list is 𝓤. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil && x.typ == nil { + return true + } + } + return false +} + +// norm returns the normal form of xl. +func (xl termlist) norm() termlist { + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + used := make([]bool, len(xl)) + var rl termlist + for i, xi := range xl { + if xi == nil || used[i] { + continue + } + for j := i + 1; j < len(xl); j++ { + xj := xl[j] + if xj == nil || used[j] { + continue + } + if u1, u2 := xi.union(xj); u2 == nil { + // If we encounter a 𝓤 term, the entire list is 𝓤. + // Exit early. + // (Note that this is not just an optimization; + // if we continue, we may end up with a 𝓤 term + // and other terms and the result would not be + // in normal form.) + if u1.typ == nil { + return allTermlist + } + xi = u1 + used[j] = true // xj is now unioned into xi - ignore it in future iterations + } + } + rl = append(rl, xi) + } + return rl +} + +// union returns the union xl ∪ yl. +func (xl termlist) union(yl termlist) termlist { + return append(xl, yl...).norm() +} + +// intersect returns the intersection xl ∩ yl. +func (xl termlist) intersect(yl termlist) termlist { + if xl.isEmpty() || yl.isEmpty() { + return nil + } + + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + var rl termlist + for _, x := range xl { + for _, y := range yl { + if r := x.intersect(y); r != nil { + rl = append(rl, r) + } + } + } + return rl.norm() +} + +// equal reports whether xl and yl represent the same type set. +func (xl termlist) equal(yl termlist) bool { + // TODO(gri) this should be more efficient + return xl.subsetOf(yl) && yl.subsetOf(xl) +} + +// includes reports whether t ∈ xl. +func (xl termlist) includes(t types.Type) bool { + for _, x := range xl { + if x.includes(t) { + return true + } + } + return false +} + +// supersetOf reports whether y ⊆ xl. +func (xl termlist) supersetOf(y *term) bool { + for _, x := range xl { + if y.subsetOf(x) { + return true + } + } + return false +} + +// subsetOf reports whether xl ⊆ yl. +func (xl termlist) subsetOf(yl termlist) bool { + if yl.isEmpty() { + return xl.isEmpty() + } + + // each term x of xl must be a subset of yl + for _, x := range xl { + if !yl.supersetOf(x) { + return false // x is not a subset yl + } + } + return true +} diff --git a/gopls/golang_org_x_tools/typeparams/typeparams_go117.go b/gopls/golang_org_x_tools/typeparams/typeparams_go117.go new file mode 100644 index 0000000..b478897 --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/typeparams_go117.go @@ -0,0 +1,197 @@ +// 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 typeparams + +import ( + "go/ast" + "go/token" + "go/types" +) + +func unsupported() { + panic("type parameters are unsupported at this go version") +} + +// IndexListExpr is a placeholder type, as type parameters are not supported at +// this Go version. Its methods panic on use. +type IndexListExpr struct { + ast.Expr + X ast.Expr // expression + Lbrack token.Pos // position of "[" + Indices []ast.Expr // index expressions + Rbrack token.Pos // position of "]" +} + +// ForTypeSpec returns an empty field list, as type parameters on not supported +// at this Go version. +func ForTypeSpec(*ast.TypeSpec) *ast.FieldList { + return nil +} + +// ForFuncType returns an empty field list, as type parameters are not +// supported at this Go version. +func ForFuncType(*ast.FuncType) *ast.FieldList { + return nil +} + +// TypeParam is a placeholder type, as type parameters are not supported at +// this Go version. Its methods panic on use. +type TypeParam struct{ types.Type } + +func (*TypeParam) Index() int { unsupported(); return 0 } +func (*TypeParam) Constraint() types.Type { unsupported(); return nil } +func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil } + +// TypeParamList is a placeholder for an empty type parameter list. +type TypeParamList struct{} + +func (*TypeParamList) Len() int { return 0 } +func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil } + +// TypeList is a placeholder for an empty type list. +type TypeList struct{} + +func (*TypeList) Len() int { return 0 } +func (*TypeList) At(int) types.Type { unsupported(); return nil } + +// NewTypeParam is unsupported at this Go version, and panics. +func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { + unsupported() + return nil +} + +// SetTypeParamConstraint is unsupported at this Go version, and panics. +func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { + unsupported() +} + +// NewSignatureType calls types.NewSignature, panicking if recvTypeParams or +// typeParams is non-empty. +func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { + if len(recvTypeParams) != 0 || len(typeParams) != 0 { + panic("signatures cannot have type parameters at this Go version") + } + return types.NewSignature(recv, params, results, variadic) +} + +// ForSignature returns an empty slice. +func ForSignature(*types.Signature) *TypeParamList { + return nil +} + +// RecvTypeParams returns a nil slice. +func RecvTypeParams(sig *types.Signature) *TypeParamList { + return nil +} + +// IsComparable returns false, as no interfaces are type-restricted at this Go +// version. +func IsComparable(*types.Interface) bool { + return false +} + +// IsMethodSet returns true, as no interfaces are type-restricted at this Go +// version. +func IsMethodSet(*types.Interface) bool { + return true +} + +// IsImplicit returns false, as no interfaces are implicit at this Go version. +func IsImplicit(*types.Interface) bool { + return false +} + +// MarkImplicit does nothing, because this Go version does not have implicit +// interfaces. +func MarkImplicit(*types.Interface) {} + +// ForNamed returns an empty type parameter list, as type parameters are not +// supported at this Go version. +func ForNamed(*types.Named) *TypeParamList { + return nil +} + +// SetForNamed panics if tparams is non-empty. +func SetForNamed(_ *types.Named, tparams []*TypeParam) { + if len(tparams) > 0 { + unsupported() + } +} + +// NamedTypeArgs returns nil. +func NamedTypeArgs(*types.Named) *TypeList { + return nil +} + +// NamedTypeOrigin is the identity method at this Go version. +func NamedTypeOrigin(named *types.Named) types.Type { + return named +} + +// Term holds information about a structural type restriction. +type Term struct { + tilde bool + typ types.Type +} + +func (m *Term) Tilde() bool { return m.tilde } +func (m *Term) Type() types.Type { return m.typ } +func (m *Term) String() string { + pre := "" + if m.tilde { + pre = "~" + } + return pre + m.typ.String() +} + +// NewTerm is unsupported at this Go version, and panics. +func NewTerm(tilde bool, typ types.Type) *Term { + return &Term{tilde, typ} +} + +// Union is a placeholder type, as type parameters are not supported at this Go +// version. Its methods panic on use. +type Union struct{ types.Type } + +func (*Union) Len() int { return 0 } +func (*Union) Term(i int) *Term { unsupported(); return nil } + +// NewUnion is unsupported at this Go version, and panics. +func NewUnion(terms []*Term) *Union { + unsupported() + return nil +} + +// InitInstanceInfo is a noop at this Go version. +func InitInstanceInfo(*types.Info) {} + +// Instance is a placeholder type, as type parameters are not supported at this +// Go version. +type Instance struct { + TypeArgs *TypeList + Type types.Type +} + +// GetInstances returns a nil map, as type parameters are not supported at this +// Go version. +func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil } + +// Context is a placeholder type, as type parameters are not supported at +// this Go version. +type Context struct{} + +// NewContext returns a placeholder Context instance. +func NewContext() *Context { + return &Context{} +} + +// Instantiate is unsupported on this Go version, and panics. +func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { + unsupported() + return nil, nil +} diff --git a/gopls/golang_org_x_tools/typeparams/typeparams_go118.go b/gopls/golang_org_x_tools/typeparams/typeparams_go118.go new file mode 100644 index 0000000..114a36b --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/typeparams_go118.go @@ -0,0 +1,151 @@ +// 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 typeparams + +import ( + "go/ast" + "go/types" +) + +// IndexListExpr is an alias for ast.IndexListExpr. +type IndexListExpr = ast.IndexListExpr + +// ForTypeSpec returns n.TypeParams. +func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList { + if n == nil { + return nil + } + return n.TypeParams +} + +// ForFuncType returns n.TypeParams. +func ForFuncType(n *ast.FuncType) *ast.FieldList { + if n == nil { + return nil + } + return n.TypeParams +} + +// TypeParam is an alias for types.TypeParam +type TypeParam = types.TypeParam + +// TypeParamList is an alias for types.TypeParamList +type TypeParamList = types.TypeParamList + +// TypeList is an alias for types.TypeList +type TypeList = types.TypeList + +// NewTypeParam calls types.NewTypeParam. +func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { + return types.NewTypeParam(name, constraint) +} + +// SetTypeParamConstraint calls tparam.SetConstraint(constraint). +func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { + tparam.SetConstraint(constraint) +} + +// NewSignatureType calls types.NewSignatureType. +func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { + return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic) +} + +// ForSignature returns sig.TypeParams() +func ForSignature(sig *types.Signature) *TypeParamList { + return sig.TypeParams() +} + +// RecvTypeParams returns sig.RecvTypeParams(). +func RecvTypeParams(sig *types.Signature) *TypeParamList { + return sig.RecvTypeParams() +} + +// IsComparable calls iface.IsComparable(). +func IsComparable(iface *types.Interface) bool { + return iface.IsComparable() +} + +// IsMethodSet calls iface.IsMethodSet(). +func IsMethodSet(iface *types.Interface) bool { + return iface.IsMethodSet() +} + +// IsImplicit calls iface.IsImplicit(). +func IsImplicit(iface *types.Interface) bool { + return iface.IsImplicit() +} + +// MarkImplicit calls iface.MarkImplicit(). +func MarkImplicit(iface *types.Interface) { + iface.MarkImplicit() +} + +// ForNamed extracts the (possibly empty) type parameter object list from +// named. +func ForNamed(named *types.Named) *TypeParamList { + return named.TypeParams() +} + +// SetForNamed sets the type params tparams on n. Each tparam must be of +// dynamic type *types.TypeParam. +func SetForNamed(n *types.Named, tparams []*TypeParam) { + n.SetTypeParams(tparams) +} + +// NamedTypeArgs returns named.TypeArgs(). +func NamedTypeArgs(named *types.Named) *TypeList { + return named.TypeArgs() +} + +// NamedTypeOrigin returns named.Orig(). +func NamedTypeOrigin(named *types.Named) types.Type { + return named.Origin() +} + +// Term is an alias for types.Term. +type Term = types.Term + +// NewTerm calls types.NewTerm. +func NewTerm(tilde bool, typ types.Type) *Term { + return types.NewTerm(tilde, typ) +} + +// Union is an alias for types.Union +type Union = types.Union + +// NewUnion calls types.NewUnion. +func NewUnion(terms []*Term) *Union { + return types.NewUnion(terms) +} + +// InitInstanceInfo initializes info to record information about type and +// function instances. +func InitInstanceInfo(info *types.Info) { + info.Instances = make(map[*ast.Ident]types.Instance) +} + +// Instance is an alias for types.Instance. +type Instance = types.Instance + +// GetInstances returns info.Instances. +func GetInstances(info *types.Info) map[*ast.Ident]Instance { + return info.Instances +} + +// Context is an alias for types.Context. +type Context = types.Context + +// NewContext calls types.NewContext. +func NewContext() *Context { + return types.NewContext() +} + +// Instantiate calls types.Instantiate. +func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { + return types.Instantiate(ctxt, typ, targs, validate) +} diff --git a/gopls/golang_org_x_tools/typeparams/typeterm.go b/gopls/golang_org_x_tools/typeparams/typeterm.go new file mode 100644 index 0000000..7ddee28 --- /dev/null +++ b/gopls/golang_org_x_tools/typeparams/typeterm.go @@ -0,0 +1,170 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by copytermlist.go DO NOT EDIT. + +package typeparams + +import "go/types" + +// A term describes elementary type sets: +// +// ∅: (*term)(nil) == ∅ // set of no types (empty set) +// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t +// +type term struct { + tilde bool // valid if typ != nil + typ types.Type +} + +func (x *term) String() string { + switch { + case x == nil: + return "∅" + case x.typ == nil: + return "𝓤" + case x.tilde: + return "~" + x.typ.String() + default: + return x.typ.String() + } +} + +// equal reports whether x and y represent the same type set. +func (x *term) equal(y *term) bool { + // easy cases + switch { + case x == nil || y == nil: + return x == y + case x.typ == nil || y.typ == nil: + return x.typ == y.typ + } + // ∅ ⊂ x, y ⊂ 𝓤 + + return x.tilde == y.tilde && types.Identical(x.typ, y.typ) +} + +// union returns the union x ∪ y: zero, one, or two non-nil terms. +func (x *term) union(y *term) (_, _ *term) { + // easy cases + switch { + case x == nil && y == nil: + return nil, nil // ∅ ∪ ∅ == ∅ + case x == nil: + return y, nil // ∅ ∪ y == y + case y == nil: + return x, nil // x ∪ ∅ == x + case x.typ == nil: + return x, nil // 𝓤 ∪ y == 𝓤 + case y.typ == nil: + return y, nil // x ∪ 𝓤 == 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return x, y // x ∪ y == (x, y) if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∪ ~t == ~t + // ~t ∪ T == ~t + // T ∪ ~t == ~t + // T ∪ T == T + if x.tilde || !y.tilde { + return x, nil + } + return y, nil +} + +// intersect returns the intersection x ∩ y. +func (x *term) intersect(y *term) *term { + // easy cases + switch { + case x == nil || y == nil: + return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅ + case x.typ == nil: + return y // 𝓤 ∩ y == y + case y.typ == nil: + return x // x ∩ 𝓤 == x + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return nil // x ∩ y == ∅ if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∩ ~t == ~t + // ~t ∩ T == T + // T ∩ ~t == T + // T ∩ T == T + if !x.tilde || y.tilde { + return x + } + return y +} + +// includes reports whether t ∈ x. +func (x *term) includes(t types.Type) bool { + // easy cases + switch { + case x == nil: + return false // t ∈ ∅ == false + case x.typ == nil: + return true // t ∈ 𝓤 == true + } + // ∅ ⊂ x ⊂ 𝓤 + + u := t + if x.tilde { + u = under(u) + } + return types.Identical(x.typ, u) +} + +// subsetOf reports whether x ⊆ y. +func (x *term) subsetOf(y *term) bool { + // easy cases + switch { + case x == nil: + return true // ∅ ⊆ y == true + case y == nil: + return false // x ⊆ ∅ == false since x != ∅ + case y.typ == nil: + return true // x ⊆ 𝓤 == true + case x.typ == nil: + return false // 𝓤 ⊆ y == false since y != 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return false // x ⊆ y == false if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ⊆ ~t == true + // ~t ⊆ T == false + // T ⊆ ~t == true + // T ⊆ T == true + return !x.tilde || y.tilde +} + +// disjoint reports whether x ∩ y == ∅. +// x.typ and y.typ must not be nil. +func (x *term) disjoint(y *term) bool { + if debug && (x.typ == nil || y.typ == nil) { + panic("invalid argument(s)") + } + ux := x.typ + if y.tilde { + ux = under(ux) + } + uy := y.typ + if x.tilde { + uy = under(uy) + } + return !types.Identical(ux, uy) +} diff --git a/gopls/golang_org_x_tools/typesinternal/errorcode.go b/gopls/golang_org_x_tools/typesinternal/errorcode.go new file mode 100644 index 0000000..0748407 --- /dev/null +++ b/gopls/golang_org_x_tools/typesinternal/errorcode.go @@ -0,0 +1,1560 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +//go:generate stringer -type=ErrorCode + +type ErrorCode int + +// This file defines the error codes that can be produced during type-checking. +// Collectively, these codes provide an identifier that may be used to +// implement special handling for certain types of errors. +// +// Error codes should be fine-grained enough that the exact nature of the error +// can be easily determined, but coarse enough that they are not an +// implementation detail of the type checking algorithm. As a rule-of-thumb, +// errors should be considered equivalent if there is a theoretical refactoring +// of the type checker in which they are emitted in exactly one place. For +// example, the type checker emits different error messages for "too many +// arguments" and "too few arguments", but one can imagine an alternative type +// checker where this check instead just emits a single "wrong number of +// arguments", so these errors should have the same code. +// +// Error code names should be as brief as possible while retaining accuracy and +// distinctiveness. In most cases names should start with an adjective +// describing the nature of the error (e.g. "invalid", "unused", "misplaced"), +// and end with a noun identifying the relevant language object. For example, +// "DuplicateDecl" or "InvalidSliceExpr". For brevity, naming follows the +// convention that "bad" implies a problem with syntax, and "invalid" implies a +// problem with types. + +const ( + // InvalidSyntaxTree occurs if an invalid syntax tree is provided + // to the type checker. It should never happen. + InvalidSyntaxTree ErrorCode = -1 +) + +const ( + _ ErrorCode = iota + + // Test is reserved for errors that only apply while in self-test mode. + Test + + /* package names */ + + // BlankPkgName occurs when a package name is the blank identifier "_". + // + // Per the spec: + // "The PackageName must not be the blank identifier." + BlankPkgName + + // MismatchedPkgName occurs when a file's package name doesn't match the + // package name already established by other files. + MismatchedPkgName + + // InvalidPkgUse occurs when a package identifier is used outside of a + // selector expression. + // + // Example: + // import "fmt" + // + // var _ = fmt + InvalidPkgUse + + /* imports */ + + // BadImportPath occurs when an import path is not valid. + BadImportPath + + // BrokenImport occurs when importing a package fails. + // + // Example: + // import "amissingpackage" + BrokenImport + + // ImportCRenamed occurs when the special import "C" is renamed. "C" is a + // pseudo-package, and must not be renamed. + // + // Example: + // import _ "C" + ImportCRenamed + + // UnusedImport occurs when an import is unused. + // + // Example: + // import "fmt" + // + // func main() {} + UnusedImport + + /* initialization */ + + // InvalidInitCycle occurs when an invalid cycle is detected within the + // initialization graph. + // + // Example: + // var x int = f() + // + // func f() int { return x } + InvalidInitCycle + + /* decls */ + + // DuplicateDecl occurs when an identifier is declared multiple times. + // + // Example: + // var x = 1 + // var x = 2 + DuplicateDecl + + // InvalidDeclCycle occurs when a declaration cycle is not valid. + // + // Example: + // import "unsafe" + // + // type T struct { + // a [n]int + // } + // + // var n = unsafe.Sizeof(T{}) + InvalidDeclCycle + + // InvalidTypeCycle occurs when a cycle in type definitions results in a + // type that is not well-defined. + // + // Example: + // import "unsafe" + // + // type T [unsafe.Sizeof(T{})]int + InvalidTypeCycle + + /* decls > const */ + + // InvalidConstInit occurs when a const declaration has a non-constant + // initializer. + // + // Example: + // var x int + // const _ = x + InvalidConstInit + + // InvalidConstVal occurs when a const value cannot be converted to its + // target type. + // + // TODO(findleyr): this error code and example are not very clear. Consider + // removing it. + // + // Example: + // const _ = 1 << "hello" + InvalidConstVal + + // InvalidConstType occurs when the underlying type in a const declaration + // is not a valid constant type. + // + // Example: + // const c *int = 4 + InvalidConstType + + /* decls > var (+ other variable assignment codes) */ + + // UntypedNilUse occurs when the predeclared (untyped) value nil is used to + // initialize a variable declared without an explicit type. + // + // Example: + // var x = nil + UntypedNilUse + + // WrongAssignCount occurs when the number of values on the right-hand side + // of an assignment or or initialization expression does not match the number + // of variables on the left-hand side. + // + // Example: + // var x = 1, 2 + WrongAssignCount + + // UnassignableOperand occurs when the left-hand side of an assignment is + // not assignable. + // + // Example: + // func f() { + // const c = 1 + // c = 2 + // } + UnassignableOperand + + // NoNewVar occurs when a short variable declaration (':=') does not declare + // new variables. + // + // Example: + // func f() { + // x := 1 + // x := 2 + // } + NoNewVar + + // MultiValAssignOp occurs when an assignment operation (+=, *=, etc) does + // not have single-valued left-hand or right-hand side. + // + // Per the spec: + // "In assignment operations, both the left- and right-hand expression lists + // must contain exactly one single-valued expression" + // + // Example: + // func f() int { + // x, y := 1, 2 + // x, y += 1 + // return x + y + // } + MultiValAssignOp + + // InvalidIfaceAssign occurs when a value of type T is used as an + // interface, but T does not implement a method of the expected interface. + // + // Example: + // type I interface { + // f() + // } + // + // type T int + // + // var x I = T(1) + InvalidIfaceAssign + + // InvalidChanAssign occurs when a chan assignment is invalid. + // + // Per the spec, a value x is assignable to a channel type T if: + // "x is a bidirectional channel value, T is a channel type, x's type V and + // T have identical element types, and at least one of V or T is not a + // defined type." + // + // Example: + // type T1 chan int + // type T2 chan int + // + // var x T1 + // // Invalid assignment because both types are named + // var _ T2 = x + InvalidChanAssign + + // IncompatibleAssign occurs when the type of the right-hand side expression + // in an assignment cannot be assigned to the type of the variable being + // assigned. + // + // Example: + // var x []int + // var _ int = x + IncompatibleAssign + + // UnaddressableFieldAssign occurs when trying to assign to a struct field + // in a map value. + // + // Example: + // func f() { + // m := make(map[string]struct{i int}) + // m["foo"].i = 42 + // } + UnaddressableFieldAssign + + /* decls > type (+ other type expression codes) */ + + // NotAType occurs when the identifier used as the underlying type in a type + // declaration or the right-hand side of a type alias does not denote a type. + // + // Example: + // var S = 2 + // + // type T S + NotAType + + // InvalidArrayLen occurs when an array length is not a constant value. + // + // Example: + // var n = 3 + // var _ = [n]int{} + InvalidArrayLen + + // BlankIfaceMethod occurs when a method name is '_'. + // + // Per the spec: + // "The name of each explicitly specified method must be unique and not + // blank." + // + // Example: + // type T interface { + // _(int) + // } + BlankIfaceMethod + + // IncomparableMapKey occurs when a map key type does not support the == and + // != operators. + // + // Per the spec: + // "The comparison operators == and != must be fully defined for operands of + // the key type; thus the key type must not be a function, map, or slice." + // + // Example: + // var x map[T]int + // + // type T []int + IncomparableMapKey + + // InvalidIfaceEmbed occurs when a non-interface type is embedded in an + // interface. + // + // Example: + // type T struct {} + // + // func (T) m() + // + // type I interface { + // T + // } + InvalidIfaceEmbed + + // InvalidPtrEmbed occurs when an embedded field is of the pointer form *T, + // and T itself is itself a pointer, an unsafe.Pointer, or an interface. + // + // Per the spec: + // "An embedded field must be specified as a type name T or as a pointer to + // a non-interface type name *T, and T itself may not be a pointer type." + // + // Example: + // type T *int + // + // type S struct { + // *T + // } + InvalidPtrEmbed + + /* decls > func and method */ + + // BadRecv occurs when a method declaration does not have exactly one + // receiver parameter. + // + // Example: + // func () _() {} + BadRecv + + // InvalidRecv occurs when a receiver type expression is not of the form T + // or *T, or T is a pointer type. + // + // Example: + // type T struct {} + // + // func (**T) m() {} + InvalidRecv + + // DuplicateFieldAndMethod occurs when an identifier appears as both a field + // and method name. + // + // Example: + // type T struct { + // m int + // } + // + // func (T) m() {} + DuplicateFieldAndMethod + + // DuplicateMethod occurs when two methods on the same receiver type have + // the same name. + // + // Example: + // type T struct {} + // func (T) m() {} + // func (T) m(i int) int { return i } + DuplicateMethod + + /* decls > special */ + + // InvalidBlank occurs when a blank identifier is used as a value or type. + // + // Per the spec: + // "The blank identifier may appear as an operand only on the left-hand side + // of an assignment." + // + // Example: + // var x = _ + InvalidBlank + + // InvalidIota occurs when the predeclared identifier iota is used outside + // of a constant declaration. + // + // Example: + // var x = iota + InvalidIota + + // MissingInitBody occurs when an init function is missing its body. + // + // Example: + // func init() + MissingInitBody + + // InvalidInitSig occurs when an init function declares parameters or + // results. + // + // Example: + // func init() int { return 1 } + InvalidInitSig + + // InvalidInitDecl occurs when init is declared as anything other than a + // function. + // + // Example: + // var init = 1 + InvalidInitDecl + + // InvalidMainDecl occurs when main is declared as anything other than a + // function, in a main package. + InvalidMainDecl + + /* exprs */ + + // TooManyValues occurs when a function returns too many values for the + // expression context in which it is used. + // + // Example: + // func ReturnTwo() (int, int) { + // return 1, 2 + // } + // + // var x = ReturnTwo() + TooManyValues + + // NotAnExpr occurs when a type expression is used where a value expression + // is expected. + // + // Example: + // type T struct {} + // + // func f() { + // T + // } + NotAnExpr + + /* exprs > const */ + + // TruncatedFloat occurs when a float constant is truncated to an integer + // value. + // + // Example: + // var _ int = 98.6 + TruncatedFloat + + // NumericOverflow occurs when a numeric constant overflows its target type. + // + // Example: + // var x int8 = 1000 + NumericOverflow + + /* exprs > operation */ + + // UndefinedOp occurs when an operator is not defined for the type(s) used + // in an operation. + // + // Example: + // var c = "a" - "b" + UndefinedOp + + // MismatchedTypes occurs when operand types are incompatible in a binary + // operation. + // + // Example: + // var a = "hello" + // var b = 1 + // var c = a - b + MismatchedTypes + + // DivByZero occurs when a division operation is provable at compile + // time to be a division by zero. + // + // Example: + // const divisor = 0 + // var x int = 1/divisor + DivByZero + + // NonNumericIncDec occurs when an increment or decrement operator is + // applied to a non-numeric value. + // + // Example: + // func f() { + // var c = "c" + // c++ + // } + NonNumericIncDec + + /* exprs > ptr */ + + // UnaddressableOperand occurs when the & operator is applied to an + // unaddressable expression. + // + // Example: + // var x = &1 + UnaddressableOperand + + // InvalidIndirection occurs when a non-pointer value is indirected via the + // '*' operator. + // + // Example: + // var x int + // var y = *x + InvalidIndirection + + /* exprs > [] */ + + // NonIndexableOperand occurs when an index operation is applied to a value + // that cannot be indexed. + // + // Example: + // var x = 1 + // var y = x[1] + NonIndexableOperand + + // InvalidIndex occurs when an index argument is not of integer type, + // negative, or out-of-bounds. + // + // Example: + // var s = [...]int{1,2,3} + // var x = s[5] + // + // Example: + // var s = []int{1,2,3} + // var _ = s[-1] + // + // Example: + // var s = []int{1,2,3} + // var i string + // var _ = s[i] + InvalidIndex + + // SwappedSliceIndices occurs when constant indices in a slice expression + // are decreasing in value. + // + // Example: + // var _ = []int{1,2,3}[2:1] + SwappedSliceIndices + + /* operators > slice */ + + // NonSliceableOperand occurs when a slice operation is applied to a value + // whose type is not sliceable, or is unaddressable. + // + // Example: + // var x = [...]int{1, 2, 3}[:1] + // + // Example: + // var x = 1 + // var y = 1[:1] + NonSliceableOperand + + // InvalidSliceExpr occurs when a three-index slice expression (a[x:y:z]) is + // applied to a string. + // + // Example: + // var s = "hello" + // var x = s[1:2:3] + InvalidSliceExpr + + /* exprs > shift */ + + // InvalidShiftCount occurs when the right-hand side of a shift operation is + // either non-integer, negative, or too large. + // + // Example: + // var ( + // x string + // y int = 1 << x + // ) + InvalidShiftCount + + // InvalidShiftOperand occurs when the shifted operand is not an integer. + // + // Example: + // var s = "hello" + // var x = s << 2 + InvalidShiftOperand + + /* exprs > chan */ + + // InvalidReceive occurs when there is a channel receive from a value that + // is either not a channel, or is a send-only channel. + // + // Example: + // func f() { + // var x = 1 + // <-x + // } + InvalidReceive + + // InvalidSend occurs when there is a channel send to a value that is not a + // channel, or is a receive-only channel. + // + // Example: + // func f() { + // var x = 1 + // x <- "hello!" + // } + InvalidSend + + /* exprs > literal */ + + // DuplicateLitKey occurs when an index is duplicated in a slice, array, or + // map literal. + // + // Example: + // var _ = []int{0:1, 0:2} + // + // Example: + // var _ = map[string]int{"a": 1, "a": 2} + DuplicateLitKey + + // MissingLitKey occurs when a map literal is missing a key expression. + // + // Example: + // var _ = map[string]int{1} + MissingLitKey + + // InvalidLitIndex occurs when the key in a key-value element of a slice or + // array literal is not an integer constant. + // + // Example: + // var i = 0 + // var x = []string{i: "world"} + InvalidLitIndex + + // OversizeArrayLit occurs when an array literal exceeds its length. + // + // Example: + // var _ = [2]int{1,2,3} + OversizeArrayLit + + // MixedStructLit occurs when a struct literal contains a mix of positional + // and named elements. + // + // Example: + // var _ = struct{i, j int}{i: 1, 2} + MixedStructLit + + // InvalidStructLit occurs when a positional struct literal has an incorrect + // number of values. + // + // Example: + // var _ = struct{i, j int}{1,2,3} + InvalidStructLit + + // MissingLitField occurs when a struct literal refers to a field that does + // not exist on the struct type. + // + // Example: + // var _ = struct{i int}{j: 2} + MissingLitField + + // DuplicateLitField occurs when a struct literal contains duplicated + // fields. + // + // Example: + // var _ = struct{i int}{i: 1, i: 2} + DuplicateLitField + + // UnexportedLitField occurs when a positional struct literal implicitly + // assigns an unexported field of an imported type. + UnexportedLitField + + // InvalidLitField occurs when a field name is not a valid identifier. + // + // Example: + // var _ = struct{i int}{1: 1} + InvalidLitField + + // UntypedLit occurs when a composite literal omits a required type + // identifier. + // + // Example: + // type outer struct{ + // inner struct { i int } + // } + // + // var _ = outer{inner: {1}} + UntypedLit + + // InvalidLit occurs when a composite literal expression does not match its + // type. + // + // Example: + // type P *struct{ + // x int + // } + // var _ = P {} + InvalidLit + + /* exprs > selector */ + + // AmbiguousSelector occurs when a selector is ambiguous. + // + // Example: + // type E1 struct { i int } + // type E2 struct { i int } + // type T struct { E1; E2 } + // + // var x T + // var _ = x.i + AmbiguousSelector + + // UndeclaredImportedName occurs when a package-qualified identifier is + // undeclared by the imported package. + // + // Example: + // import "go/types" + // + // var _ = types.NotAnActualIdentifier + UndeclaredImportedName + + // UnexportedName occurs when a selector refers to an unexported identifier + // of an imported package. + // + // Example: + // import "reflect" + // + // type _ reflect.flag + UnexportedName + + // UndeclaredName occurs when an identifier is not declared in the current + // scope. + // + // Example: + // var x T + UndeclaredName + + // MissingFieldOrMethod occurs when a selector references a field or method + // that does not exist. + // + // Example: + // type T struct {} + // + // var x = T{}.f + MissingFieldOrMethod + + /* exprs > ... */ + + // BadDotDotDotSyntax occurs when a "..." occurs in a context where it is + // not valid. + // + // Example: + // var _ = map[int][...]int{0: {}} + BadDotDotDotSyntax + + // NonVariadicDotDotDot occurs when a "..." is used on the final argument to + // a non-variadic function. + // + // Example: + // func printArgs(s []string) { + // for _, a := range s { + // println(a) + // } + // } + // + // func f() { + // s := []string{"a", "b", "c"} + // printArgs(s...) + // } + NonVariadicDotDotDot + + // MisplacedDotDotDot occurs when a "..." is used somewhere other than the + // final argument to a function call. + // + // Example: + // func printArgs(args ...int) { + // for _, a := range args { + // println(a) + // } + // } + // + // func f() { + // a := []int{1,2,3} + // printArgs(0, a...) + // } + MisplacedDotDotDot + + // InvalidDotDotDotOperand occurs when a "..." operator is applied to a + // single-valued operand. + // + // Example: + // func printArgs(args ...int) { + // for _, a := range args { + // println(a) + // } + // } + // + // func f() { + // a := 1 + // printArgs(a...) + // } + // + // Example: + // func args() (int, int) { + // return 1, 2 + // } + // + // func printArgs(args ...int) { + // for _, a := range args { + // println(a) + // } + // } + // + // func g() { + // printArgs(args()...) + // } + InvalidDotDotDotOperand + + // InvalidDotDotDot occurs when a "..." is used in a non-variadic built-in + // function. + // + // Example: + // var s = []int{1, 2, 3} + // var l = len(s...) + InvalidDotDotDot + + /* exprs > built-in */ + + // UncalledBuiltin occurs when a built-in function is used as a + // function-valued expression, instead of being called. + // + // Per the spec: + // "The built-in functions do not have standard Go types, so they can only + // appear in call expressions; they cannot be used as function values." + // + // Example: + // var _ = copy + UncalledBuiltin + + // InvalidAppend occurs when append is called with a first argument that is + // not a slice. + // + // Example: + // var _ = append(1, 2) + InvalidAppend + + // InvalidCap occurs when an argument to the cap built-in function is not of + // supported type. + // + // See https://golang.org/ref/spec#Lengthand_capacity for information on + // which underlying types are supported as arguments to cap and len. + // + // Example: + // var s = 2 + // var x = cap(s) + InvalidCap + + // InvalidClose occurs when close(...) is called with an argument that is + // not of channel type, or that is a receive-only channel. + // + // Example: + // func f() { + // var x int + // close(x) + // } + InvalidClose + + // InvalidCopy occurs when the arguments are not of slice type or do not + // have compatible type. + // + // See https://golang.org/ref/spec#Appendingand_copying_slices for more + // information on the type requirements for the copy built-in. + // + // Example: + // func f() { + // var x []int + // y := []int64{1,2,3} + // copy(x, y) + // } + InvalidCopy + + // InvalidComplex occurs when the complex built-in function is called with + // arguments with incompatible types. + // + // Example: + // var _ = complex(float32(1), float64(2)) + InvalidComplex + + // InvalidDelete occurs when the delete built-in function is called with a + // first argument that is not a map. + // + // Example: + // func f() { + // m := "hello" + // delete(m, "e") + // } + InvalidDelete + + // InvalidImag occurs when the imag built-in function is called with an + // argument that does not have complex type. + // + // Example: + // var _ = imag(int(1)) + InvalidImag + + // InvalidLen occurs when an argument to the len built-in function is not of + // supported type. + // + // See https://golang.org/ref/spec#Lengthand_capacity for information on + // which underlying types are supported as arguments to cap and len. + // + // Example: + // var s = 2 + // var x = len(s) + InvalidLen + + // SwappedMakeArgs occurs when make is called with three arguments, and its + // length argument is larger than its capacity argument. + // + // Example: + // var x = make([]int, 3, 2) + SwappedMakeArgs + + // InvalidMake occurs when make is called with an unsupported type argument. + // + // See https://golang.org/ref/spec#Makingslices_maps_and_channels for + // information on the types that may be created using make. + // + // Example: + // var x = make(int) + InvalidMake + + // InvalidReal occurs when the real built-in function is called with an + // argument that does not have complex type. + // + // Example: + // var _ = real(int(1)) + InvalidReal + + /* exprs > assertion */ + + // InvalidAssert occurs when a type assertion is applied to a + // value that is not of interface type. + // + // Example: + // var x = 1 + // var _ = x.(float64) + InvalidAssert + + // ImpossibleAssert occurs for a type assertion x.(T) when the value x of + // interface cannot have dynamic type T, due to a missing or mismatching + // method on T. + // + // Example: + // type T int + // + // func (t *T) m() int { return int(*t) } + // + // type I interface { m() int } + // + // var x I + // var _ = x.(T) + ImpossibleAssert + + /* exprs > conversion */ + + // InvalidConversion occurs when the argument type cannot be converted to the + // target. + // + // See https://golang.org/ref/spec#Conversions for the rules of + // convertibility. + // + // Example: + // var x float64 + // var _ = string(x) + InvalidConversion + + // InvalidUntypedConversion occurs when an there is no valid implicit + // conversion from an untyped value satisfying the type constraints of the + // context in which it is used. + // + // Example: + // var _ = 1 + "" + InvalidUntypedConversion + + /* offsetof */ + + // BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument + // that is not a selector expression. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Offsetof(x) + BadOffsetofSyntax + + // InvalidOffsetof occurs when unsafe.Offsetof is called with a method + // selector, rather than a field selector, or when the field is embedded via + // a pointer. + // + // Per the spec: + // + // "If f is an embedded field, it must be reachable without pointer + // indirections through fields of the struct. " + // + // Example: + // import "unsafe" + // + // type T struct { f int } + // type S struct { *T } + // var s S + // var _ = unsafe.Offsetof(s.f) + // + // Example: + // import "unsafe" + // + // type S struct{} + // + // func (S) m() {} + // + // var s S + // var _ = unsafe.Offsetof(s.m) + InvalidOffsetof + + /* control flow > scope */ + + // UnusedExpr occurs when a side-effect free expression is used as a + // statement. Such a statement has no effect. + // + // Example: + // func f(i int) { + // i*i + // } + UnusedExpr + + // UnusedVar occurs when a variable is declared but unused. + // + // Example: + // func f() { + // x := 1 + // } + UnusedVar + + // MissingReturn occurs when a function with results is missing a return + // statement. + // + // Example: + // func f() int {} + MissingReturn + + // WrongResultCount occurs when a return statement returns an incorrect + // number of values. + // + // Example: + // func ReturnOne() int { + // return 1, 2 + // } + WrongResultCount + + // OutOfScopeResult occurs when the name of a value implicitly returned by + // an empty return statement is shadowed in a nested scope. + // + // Example: + // func factor(n int) (i int) { + // for i := 2; i < n; i++ { + // if n%i == 0 { + // return + // } + // } + // return 0 + // } + OutOfScopeResult + + /* control flow > if */ + + // InvalidCond occurs when an if condition is not a boolean expression. + // + // Example: + // func checkReturn(i int) { + // if i { + // panic("non-zero return") + // } + // } + InvalidCond + + /* control flow > for */ + + // InvalidPostDecl occurs when there is a declaration in a for-loop post + // statement. + // + // Example: + // func f() { + // for i := 0; i < 10; j := 0 {} + // } + InvalidPostDecl + + // InvalidChanRange occurs when a send-only channel used in a range + // expression. + // + // Example: + // func sum(c chan<- int) { + // s := 0 + // for i := range c { + // s += i + // } + // } + InvalidChanRange + + // InvalidIterVar occurs when two iteration variables are used while ranging + // over a channel. + // + // Example: + // func f(c chan int) { + // for k, v := range c { + // println(k, v) + // } + // } + InvalidIterVar + + // InvalidRangeExpr occurs when the type of a range expression is not array, + // slice, string, map, or channel. + // + // Example: + // func f(i int) { + // for j := range i { + // println(j) + // } + // } + InvalidRangeExpr + + /* control flow > switch */ + + // MisplacedBreak occurs when a break statement is not within a for, switch, + // or select statement of the innermost function definition. + // + // Example: + // func f() { + // break + // } + MisplacedBreak + + // MisplacedContinue occurs when a continue statement is not within a for + // loop of the innermost function definition. + // + // Example: + // func sumeven(n int) int { + // proceed := func() { + // continue + // } + // sum := 0 + // for i := 1; i <= n; i++ { + // if i % 2 != 0 { + // proceed() + // } + // sum += i + // } + // return sum + // } + MisplacedContinue + + // MisplacedFallthrough occurs when a fallthrough statement is not within an + // expression switch. + // + // Example: + // func typename(i interface{}) string { + // switch i.(type) { + // case int64: + // fallthrough + // case int: + // return "int" + // } + // return "unsupported" + // } + MisplacedFallthrough + + // DuplicateCase occurs when a type or expression switch has duplicate + // cases. + // + // Example: + // func printInt(i int) { + // switch i { + // case 1: + // println("one") + // case 1: + // println("One") + // } + // } + DuplicateCase + + // DuplicateDefault occurs when a type or expression switch has multiple + // default clauses. + // + // Example: + // func printInt(i int) { + // switch i { + // case 1: + // println("one") + // default: + // println("One") + // default: + // println("1") + // } + // } + DuplicateDefault + + // BadTypeKeyword occurs when a .(type) expression is used anywhere other + // than a type switch. + // + // Example: + // type I interface { + // m() + // } + // var t I + // var _ = t.(type) + BadTypeKeyword + + // InvalidTypeSwitch occurs when .(type) is used on an expression that is + // not of interface type. + // + // Example: + // func f(i int) { + // switch x := i.(type) {} + // } + InvalidTypeSwitch + + // InvalidExprSwitch occurs when a switch expression is not comparable. + // + // Example: + // func _() { + // var a struct{ _ func() } + // switch a /* ERROR cannot switch on a */ { + // } + // } + InvalidExprSwitch + + /* control flow > select */ + + // InvalidSelectCase occurs when a select case is not a channel send or + // receive. + // + // Example: + // func checkChan(c <-chan int) bool { + // select { + // case c: + // return true + // default: + // return false + // } + // } + InvalidSelectCase + + /* control flow > labels and jumps */ + + // UndeclaredLabel occurs when an undeclared label is jumped to. + // + // Example: + // func f() { + // goto L + // } + UndeclaredLabel + + // DuplicateLabel occurs when a label is declared more than once. + // + // Example: + // func f() int { + // L: + // L: + // return 1 + // } + DuplicateLabel + + // MisplacedLabel occurs when a break or continue label is not on a for, + // switch, or select statement. + // + // Example: + // func f() { + // L: + // a := []int{1,2,3} + // for _, e := range a { + // if e > 10 { + // break L + // } + // println(a) + // } + // } + MisplacedLabel + + // UnusedLabel occurs when a label is declared but not used. + // + // Example: + // func f() { + // L: + // } + UnusedLabel + + // JumpOverDecl occurs when a label jumps over a variable declaration. + // + // Example: + // func f() int { + // goto L + // x := 2 + // L: + // x++ + // return x + // } + JumpOverDecl + + // JumpIntoBlock occurs when a forward jump goes to a label inside a nested + // block. + // + // Example: + // func f(x int) { + // goto L + // if x > 0 { + // L: + // print("inside block") + // } + // } + JumpIntoBlock + + /* control flow > calls */ + + // InvalidMethodExpr occurs when a pointer method is called but the argument + // is not addressable. + // + // Example: + // type T struct {} + // + // func (*T) m() int { return 1 } + // + // var _ = T.m(T{}) + InvalidMethodExpr + + // WrongArgCount occurs when too few or too many arguments are passed by a + // function call. + // + // Example: + // func f(i int) {} + // var x = f() + WrongArgCount + + // InvalidCall occurs when an expression is called that is not of function + // type. + // + // Example: + // var x = "x" + // var y = x() + InvalidCall + + /* control flow > suspended */ + + // UnusedResults occurs when a restricted expression-only built-in function + // is suspended via go or defer. Such a suspension discards the results of + // these side-effect free built-in functions, and therefore is ineffectual. + // + // Example: + // func f(a []int) int { + // defer len(a) + // return i + // } + UnusedResults + + // InvalidDefer occurs when a deferred expression is not a function call, + // for example if the expression is a type conversion. + // + // Example: + // func f(i int) int { + // defer int32(i) + // return i + // } + InvalidDefer + + // InvalidGo occurs when a go expression is not a function call, for example + // if the expression is a type conversion. + // + // Example: + // func f(i int) int { + // go int32(i) + // return i + // } + InvalidGo + + // All codes below were added in Go 1.17. + + /* decl */ + + // BadDecl occurs when a declaration has invalid syntax. + BadDecl + + // RepeatedDecl occurs when an identifier occurs more than once on the left + // hand side of a short variable declaration. + // + // Example: + // func _() { + // x, y, y := 1, 2, 3 + // } + RepeatedDecl + + /* unsafe */ + + // InvalidUnsafeAdd occurs when unsafe.Add is called with a + // length argument that is not of integer type. + // + // Example: + // import "unsafe" + // + // var p unsafe.Pointer + // var _ = unsafe.Add(p, float64(1)) + InvalidUnsafeAdd + + // InvalidUnsafeSlice occurs when unsafe.Slice is called with a + // pointer argument that is not of pointer type or a length argument + // that is not of integer type, negative, or out of bounds. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(x, 1) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, float64(1)) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, -1) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, uint64(1) << 63) + InvalidUnsafeSlice + + // All codes below were added in Go 1.18. + + /* features */ + + // UnsupportedFeature occurs when a language feature is used that is not + // supported at this Go version. + UnsupportedFeature + + /* type params */ + + // NotAGenericType occurs when a non-generic type is used where a generic + // type is expected: in type or function instantiation. + // + // Example: + // type T int + // + // var _ T[int] + NotAGenericType + + // WrongTypeArgCount occurs when a type or function is instantiated with an + // incorrent number of type arguments, including when a generic type or + // function is used without instantiation. + // + // Errors inolving failed type inference are assigned other error codes. + // + // Example: + // type T[p any] int + // + // var _ T[int, string] + // + // Example: + // func f[T any]() {} + // + // var x = f + WrongTypeArgCount + + // CannotInferTypeArgs occurs when type or function type argument inference + // fails to infer all type arguments. + // + // Example: + // func f[T any]() {} + // + // func _() { + // f() + // } + // + // Example: + // type N[P, Q any] struct{} + // + // var _ N[int] + CannotInferTypeArgs + + // InvalidTypeArg occurs when a type argument does not satisfy its + // corresponding type parameter constraints. + // + // Example: + // type T[P ~int] struct{} + // + // var _ T[string] + InvalidTypeArg // arguments? InferenceFailed + + // InvalidInstanceCycle occurs when an invalid cycle is detected + // within the instantiation graph. + // + // Example: + // func f[T any]() { f[*T]() } + InvalidInstanceCycle + + // InvalidUnion occurs when an embedded union or approximation element is + // not valid. + // + // Example: + // type _ interface { + // ~int | interface{ m() } + // } + InvalidUnion + + // MisplacedConstraintIface occurs when a constraint-type interface is used + // outside of constraint position. + // + // Example: + // type I interface { ~int } + // + // var _ I + MisplacedConstraintIface + + // InvalidMethodTypeParams occurs when methods have type parameters. + // + // It cannot be encountered with an AST parsed using go/parser. + InvalidMethodTypeParams + + // MisplacedTypeParam occurs when a type parameter is used in a place where + // it is not permitted. + // + // Example: + // type T[P any] P + // + // Example: + // type T[P any] struct{ *P } + MisplacedTypeParam + + // InvalidUnsafeSliceData occurs when unsafe.SliceData is called with + // an argument that is not of slice type. It also occurs if it is used + // in a package compiled for a language version before go1.20. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.SliceData(x) + InvalidUnsafeSliceData + + // InvalidUnsafeString occurs when unsafe.String is called with + // a length argument that is not of integer type, negative, or + // out of bounds. It also occurs if it is used in a package + // compiled for a language version before go1.20. + // + // Example: + // import "unsafe" + // + // var b [10]byte + // var _ = unsafe.String(&b[0], -1) + InvalidUnsafeString + + // InvalidUnsafeStringData occurs if it is used in a package + // compiled for a language version before go1.20. + _ // not used anymore + +) diff --git a/gopls/golang_org_x_tools/typesinternal/errorcode_string.go b/gopls/golang_org_x_tools/typesinternal/errorcode_string.go new file mode 100644 index 0000000..15ecf7c --- /dev/null +++ b/gopls/golang_org_x_tools/typesinternal/errorcode_string.go @@ -0,0 +1,179 @@ +// Code generated by "stringer -type=ErrorCode"; DO NOT EDIT. + +package typesinternal + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidSyntaxTree - -1] + _ = x[Test-1] + _ = x[BlankPkgName-2] + _ = x[MismatchedPkgName-3] + _ = x[InvalidPkgUse-4] + _ = x[BadImportPath-5] + _ = x[BrokenImport-6] + _ = x[ImportCRenamed-7] + _ = x[UnusedImport-8] + _ = x[InvalidInitCycle-9] + _ = x[DuplicateDecl-10] + _ = x[InvalidDeclCycle-11] + _ = x[InvalidTypeCycle-12] + _ = x[InvalidConstInit-13] + _ = x[InvalidConstVal-14] + _ = x[InvalidConstType-15] + _ = x[UntypedNilUse-16] + _ = x[WrongAssignCount-17] + _ = x[UnassignableOperand-18] + _ = x[NoNewVar-19] + _ = x[MultiValAssignOp-20] + _ = x[InvalidIfaceAssign-21] + _ = x[InvalidChanAssign-22] + _ = x[IncompatibleAssign-23] + _ = x[UnaddressableFieldAssign-24] + _ = x[NotAType-25] + _ = x[InvalidArrayLen-26] + _ = x[BlankIfaceMethod-27] + _ = x[IncomparableMapKey-28] + _ = x[InvalidIfaceEmbed-29] + _ = x[InvalidPtrEmbed-30] + _ = x[BadRecv-31] + _ = x[InvalidRecv-32] + _ = x[DuplicateFieldAndMethod-33] + _ = x[DuplicateMethod-34] + _ = x[InvalidBlank-35] + _ = x[InvalidIota-36] + _ = x[MissingInitBody-37] + _ = x[InvalidInitSig-38] + _ = x[InvalidInitDecl-39] + _ = x[InvalidMainDecl-40] + _ = x[TooManyValues-41] + _ = x[NotAnExpr-42] + _ = x[TruncatedFloat-43] + _ = x[NumericOverflow-44] + _ = x[UndefinedOp-45] + _ = x[MismatchedTypes-46] + _ = x[DivByZero-47] + _ = x[NonNumericIncDec-48] + _ = x[UnaddressableOperand-49] + _ = x[InvalidIndirection-50] + _ = x[NonIndexableOperand-51] + _ = x[InvalidIndex-52] + _ = x[SwappedSliceIndices-53] + _ = x[NonSliceableOperand-54] + _ = x[InvalidSliceExpr-55] + _ = x[InvalidShiftCount-56] + _ = x[InvalidShiftOperand-57] + _ = x[InvalidReceive-58] + _ = x[InvalidSend-59] + _ = x[DuplicateLitKey-60] + _ = x[MissingLitKey-61] + _ = x[InvalidLitIndex-62] + _ = x[OversizeArrayLit-63] + _ = x[MixedStructLit-64] + _ = x[InvalidStructLit-65] + _ = x[MissingLitField-66] + _ = x[DuplicateLitField-67] + _ = x[UnexportedLitField-68] + _ = x[InvalidLitField-69] + _ = x[UntypedLit-70] + _ = x[InvalidLit-71] + _ = x[AmbiguousSelector-72] + _ = x[UndeclaredImportedName-73] + _ = x[UnexportedName-74] + _ = x[UndeclaredName-75] + _ = x[MissingFieldOrMethod-76] + _ = x[BadDotDotDotSyntax-77] + _ = x[NonVariadicDotDotDot-78] + _ = x[MisplacedDotDotDot-79] + _ = x[InvalidDotDotDotOperand-80] + _ = x[InvalidDotDotDot-81] + _ = x[UncalledBuiltin-82] + _ = x[InvalidAppend-83] + _ = x[InvalidCap-84] + _ = x[InvalidClose-85] + _ = x[InvalidCopy-86] + _ = x[InvalidComplex-87] + _ = x[InvalidDelete-88] + _ = x[InvalidImag-89] + _ = x[InvalidLen-90] + _ = x[SwappedMakeArgs-91] + _ = x[InvalidMake-92] + _ = x[InvalidReal-93] + _ = x[InvalidAssert-94] + _ = x[ImpossibleAssert-95] + _ = x[InvalidConversion-96] + _ = x[InvalidUntypedConversion-97] + _ = x[BadOffsetofSyntax-98] + _ = x[InvalidOffsetof-99] + _ = x[UnusedExpr-100] + _ = x[UnusedVar-101] + _ = x[MissingReturn-102] + _ = x[WrongResultCount-103] + _ = x[OutOfScopeResult-104] + _ = x[InvalidCond-105] + _ = x[InvalidPostDecl-106] + _ = x[InvalidChanRange-107] + _ = x[InvalidIterVar-108] + _ = x[InvalidRangeExpr-109] + _ = x[MisplacedBreak-110] + _ = x[MisplacedContinue-111] + _ = x[MisplacedFallthrough-112] + _ = x[DuplicateCase-113] + _ = x[DuplicateDefault-114] + _ = x[BadTypeKeyword-115] + _ = x[InvalidTypeSwitch-116] + _ = x[InvalidExprSwitch-117] + _ = x[InvalidSelectCase-118] + _ = x[UndeclaredLabel-119] + _ = x[DuplicateLabel-120] + _ = x[MisplacedLabel-121] + _ = x[UnusedLabel-122] + _ = x[JumpOverDecl-123] + _ = x[JumpIntoBlock-124] + _ = x[InvalidMethodExpr-125] + _ = x[WrongArgCount-126] + _ = x[InvalidCall-127] + _ = x[UnusedResults-128] + _ = x[InvalidDefer-129] + _ = x[InvalidGo-130] + _ = x[BadDecl-131] + _ = x[RepeatedDecl-132] + _ = x[InvalidUnsafeAdd-133] + _ = x[InvalidUnsafeSlice-134] + _ = x[UnsupportedFeature-135] + _ = x[NotAGenericType-136] + _ = x[WrongTypeArgCount-137] + _ = x[CannotInferTypeArgs-138] + _ = x[InvalidTypeArg-139] + _ = x[InvalidInstanceCycle-140] + _ = x[InvalidUnion-141] + _ = x[MisplacedConstraintIface-142] + _ = x[InvalidMethodTypeParams-143] + _ = x[MisplacedTypeParam-144] + _ = x[InvalidUnsafeSliceData-145] + _ = x[InvalidUnsafeString-146] +} + +const ( + _ErrorCode_name_0 = "InvalidSyntaxTree" + _ErrorCode_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKeyInvalidIfaceEmbedInvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDotInvalidDotDotDotOperandInvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDeclInvalidChanRangeInvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString" +) + +var ( + _ErrorCode_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411, 428, 443, 450, 461, 484, 499, 511, 522, 537, 551, 566, 581, 594, 603, 617, 632, 643, 658, 667, 683, 703, 721, 740, 752, 771, 790, 806, 823, 842, 856, 867, 882, 895, 910, 926, 940, 956, 971, 988, 1006, 1021, 1031, 1041, 1058, 1080, 1094, 1108, 1128, 1146, 1166, 1184, 1207, 1223, 1238, 1251, 1261, 1273, 1284, 1298, 1311, 1322, 1332, 1347, 1358, 1369, 1382, 1398, 1415, 1439, 1456, 1471, 1481, 1490, 1503, 1519, 1535, 1546, 1561, 1577, 1591, 1607, 1621, 1638, 1658, 1671, 1687, 1701, 1718, 1735, 1752, 1767, 1781, 1795, 1806, 1818, 1831, 1848, 1861, 1872, 1885, 1897, 1906, 1913, 1925, 1941, 1959, 1977, 1992, 2009, 2028, 2042, 2062, 2074, 2098, 2121, 2139, 2161, 2180} +) + +func (i ErrorCode) String() string { + switch { + case i == -1: + return _ErrorCode_name_0 + case 1 <= i && i <= 146: + i -= 1 + return _ErrorCode_name_1[_ErrorCode_index_1[i]:_ErrorCode_index_1[i+1]] + default: + return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/gopls/golang_org_x_tools/typesinternal/types.go b/gopls/golang_org_x_tools/typesinternal/types.go new file mode 100644 index 0000000..ce7d435 --- /dev/null +++ b/gopls/golang_org_x_tools/typesinternal/types.go @@ -0,0 +1,52 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package typesinternal provides access to internal go/types APIs that are not +// yet exported. +package typesinternal + +import ( + "go/token" + "go/types" + "reflect" + "unsafe" +) + +func SetUsesCgo(conf *types.Config) bool { + v := reflect.ValueOf(conf).Elem() + + f := v.FieldByName("go115UsesCgo") + if !f.IsValid() { + f = v.FieldByName("UsesCgo") + if !f.IsValid() { + return false + } + } + + addr := unsafe.Pointer(f.UnsafeAddr()) + *(*bool)(addr) = true + + return true +} + +// ReadGo116ErrorData extracts additional information from types.Error values +// generated by Go version 1.16 and later: the error code, start position, and +// end position. If all positions are valid, start <= err.Pos <= end. +// +// If the data could not be read, the final result parameter will be false. +func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos, ok bool) { + var data [3]int + // By coincidence all of these fields are ints, which simplifies things. + v := reflect.ValueOf(err) + for i, name := range []string{"go116code", "go116start", "go116end"} { + f := v.FieldByName(name) + if !f.IsValid() { + return 0, 0, 0, false + } + data[i] = int(f.Int()) + } + return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true +} + +var SetGoVersion = func(conf *types.Config, version string) bool { return false } diff --git a/gopls/golang_org_x_tools/typesinternal/types_118.go b/gopls/golang_org_x_tools/typesinternal/types_118.go new file mode 100644 index 0000000..a42b072 --- /dev/null +++ b/gopls/golang_org_x_tools/typesinternal/types_118.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package typesinternal + +import ( + "go/types" +) + +func init() { + SetGoVersion = func(conf *types.Config, version string) bool { + conf.GoVersion = version + return true + } +} diff --git a/gopls/golang_org_x_tools/xcontext/xcontext.go b/gopls/golang_org_x_tools/xcontext/xcontext.go new file mode 100644 index 0000000..ff8ed4e --- /dev/null +++ b/gopls/golang_org_x_tools/xcontext/xcontext.go @@ -0,0 +1,23 @@ +// 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 xcontext is a package to offer the extra functionality we need +// from contexts that is not available from the standard context package. +package xcontext + +import ( + "context" + "time" +) + +// Detach returns a context that keeps all the values of its parent context +// but detaches from the cancellation and error handling. +func Detach(ctx context.Context) context.Context { return detachedContext{ctx} } + +type detachedContext struct{ parent context.Context } + +func (v detachedContext) Deadline() (time.Time, bool) { return time.Time{}, false } +func (v detachedContext) Done() <-chan struct{} { return nil } +func (v detachedContext) Err() error { return nil } +func (v detachedContext) Value(key interface{}) interface{} { return v.parent.Value(key) } diff --git a/gopls/golang_org_x_tools_gopls/LICENSE b/gopls/golang_org_x_tools_gopls/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gopls/golang_org_x_tools_gopls/govulncheck/README.md b/gopls/golang_org_x_tools_gopls/govulncheck/README.md new file mode 100644 index 0000000..bc10d8a --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/govulncheck/README.md @@ -0,0 +1,19 @@ +# internal/govulncheck package + +This package is a literal copy of the cmd/govulncheck/internal/govulncheck +package in the vuln repo (https://go.googlesource.com/vuln). + +The `copy.sh` does the copying, after removing all .go files here. To use it: + +1. Clone the vuln repo to a directory next to the directory holding this repo + (tools). After doing that your directory structure should look something like + ``` + ~/repos/x/tools/gopls/... + ~/repos/x/vuln/... + ``` + +2. cd to this directory. + +3. Run `copy.sh`. + +4. Re-add build tags for go1.18 \ No newline at end of file diff --git a/gopls/golang_org_x_tools_gopls/govulncheck/filepath.go b/gopls/golang_org_x_tools_gopls/govulncheck/filepath.go new file mode 100644 index 0000000..cef78f5 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/govulncheck/filepath.go @@ -0,0 +1,38 @@ +// 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 govulncheck + +import ( + "path/filepath" + "strings" +) + +// AbsRelShorter takes path and returns its path relative +// to the current directory, if shorter. Returns path +// when path is an empty string or upon any error. +func AbsRelShorter(path string) string { + if path == "" { + return "" + } + + c, err := filepath.Abs(".") + if err != nil { + return path + } + r, err := filepath.Rel(c, path) + if err != nil { + return path + } + + rSegments := strings.Split(r, string(filepath.Separator)) + pathSegments := strings.Split(path, string(filepath.Separator)) + if len(rSegments) < len(pathSegments) { + return r + } + return path +} diff --git a/gopls/golang_org_x_tools_gopls/govulncheck/semver/semver.go b/gopls/golang_org_x_tools_gopls/govulncheck/semver/semver.go new file mode 100644 index 0000000..8b1cfe5 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/govulncheck/semver/semver.go @@ -0,0 +1,84 @@ +// 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 semver provides shared utilities for manipulating +// Go semantic versions. +package semver + +import ( + "regexp" + "strings" +) + +// addSemverPrefix adds a 'v' prefix to s if it isn't already prefixed +// with 'v' or 'go'. This allows us to easily test go-style SEMVER +// strings against normal SEMVER strings. +func addSemverPrefix(s string) string { + if !strings.HasPrefix(s, "v") && !strings.HasPrefix(s, "go") { + return "v" + s + } + return s +} + +// removeSemverPrefix removes the 'v' or 'go' prefixes from go-style +// SEMVER strings, for usage in the public vulnerability format. +func removeSemverPrefix(s string) string { + s = strings.TrimPrefix(s, "v") + s = strings.TrimPrefix(s, "go") + return s +} + +// CanonicalizeSemverPrefix turns a SEMVER string into the canonical +// representation using the 'v' prefix, as used by the OSV format. +// Input may be a bare SEMVER ("1.2.3"), Go prefixed SEMVER ("go1.2.3"), +// or already canonical SEMVER ("v1.2.3"). +func CanonicalizeSemverPrefix(s string) string { + return addSemverPrefix(removeSemverPrefix(s)) +} + +var ( + // Regexp for matching go tags. The groups are: + // 1 the major.minor version + // 2 the patch version, or empty if none + // 3 the entire prerelease, if present + // 4 the prerelease type ("beta" or "rc") + // 5 the prerelease number + tagRegexp = regexp.MustCompile(`^go(\d+\.\d+)(\.\d+|)((beta|rc|-pre)(\d+))?$`) +) + +// This is a modified copy of pkgsite/internal/stdlib:VersionForTag. +func GoTagToSemver(tag string) string { + if tag == "" { + return "" + } + + tag = strings.Fields(tag)[0] + // Special cases for go1. + if tag == "go1" { + return "v1.0.0" + } + if tag == "go1.0" { + return "" + } + m := tagRegexp.FindStringSubmatch(tag) + if m == nil { + return "" + } + version := "v" + m[1] + if m[2] != "" { + version += m[2] + } else { + version += ".0" + } + if m[3] != "" { + if !strings.HasPrefix(m[4], "-") { + version += "-" + } + version += m[4] + "." + m[5] + } + return version +} diff --git a/gopls/golang_org_x_tools_gopls/govulncheck/source.go b/gopls/golang_org_x_tools_gopls/govulncheck/source.go new file mode 100644 index 0000000..d3f519d --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/govulncheck/source.go @@ -0,0 +1,116 @@ +// 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 govulncheck + +import ( + "fmt" + "sort" + "strings" + + "golang.org/x/tools/go/packages" + "golang.org/x/vuln/vulncheck" +) + +// A PackageError contains errors from loading a set of packages. +type PackageError struct { + Errors []packages.Error +} + +func (e *PackageError) Error() string { + var b strings.Builder + fmt.Fprintln(&b, "Packages contain errors:") + for _, e := range e.Errors { + fmt.Fprintln(&b, e) + } + return b.String() +} + +// LoadPackages loads the packages matching patterns using cfg, after setting +// the cfg mode flags that vulncheck needs for analysis. +// If the packages contain errors, a PackageError is returned containing a list of the errors, +// along with the packages themselves. +func LoadPackages(cfg *packages.Config, patterns ...string) ([]*vulncheck.Package, error) { + cfg.Mode |= packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | + packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | + packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps | + packages.NeedModule + + pkgs, err := packages.Load(cfg, patterns...) + vpkgs := vulncheck.Convert(pkgs) + if err != nil { + return nil, err + } + var perrs []packages.Error + packages.Visit(pkgs, nil, func(p *packages.Package) { + perrs = append(perrs, p.Errors...) + }) + if len(perrs) > 0 { + err = &PackageError{perrs} + } + return vpkgs, err +} + +// CallInfo is information about calls to vulnerable functions. +type CallInfo struct { + // CallStacks contains all call stacks to vulnerable functions. + CallStacks map[*vulncheck.Vuln][]vulncheck.CallStack + + // VulnGroups contains vulnerabilities grouped by ID and package. + VulnGroups [][]*vulncheck.Vuln + + // ModuleVersions is a map of module paths to versions. + ModuleVersions map[string]string + + // TopPackages contains the top-level packages in the call info. + TopPackages map[string]bool +} + +// GetCallInfo computes call stacks and related information from a vulncheck.Result. +// It also makes a set of top-level packages from pkgs. +func GetCallInfo(r *vulncheck.Result, pkgs []*vulncheck.Package) *CallInfo { + pset := map[string]bool{} + for _, p := range pkgs { + pset[p.PkgPath] = true + } + return &CallInfo{ + CallStacks: vulncheck.CallStacks(r), + VulnGroups: groupByIDAndPackage(r.Vulns), + ModuleVersions: moduleVersionMap(r.Modules), + TopPackages: pset, + } +} + +func groupByIDAndPackage(vs []*vulncheck.Vuln) [][]*vulncheck.Vuln { + groups := map[[2]string][]*vulncheck.Vuln{} + for _, v := range vs { + key := [2]string{v.OSV.ID, v.PkgPath} + groups[key] = append(groups[key], v) + } + + var res [][]*vulncheck.Vuln + for _, g := range groups { + res = append(res, g) + } + sort.Slice(res, func(i, j int) bool { + return res[i][0].PkgPath < res[j][0].PkgPath + }) + return res +} + +// moduleVersionMap builds a map from module paths to versions. +func moduleVersionMap(mods []*vulncheck.Module) map[string]string { + moduleVersions := map[string]string{} + for _, m := range mods { + v := m.Version + if m.Replace != nil { + v = m.Replace.Version + } + moduleVersions[m.Path] = v + } + return moduleVersions +} diff --git a/gopls/golang_org_x_tools_gopls/govulncheck/types_118.go b/gopls/golang_org_x_tools_gopls/govulncheck/types_118.go new file mode 100644 index 0000000..7410963 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/govulncheck/types_118.go @@ -0,0 +1,44 @@ +// 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 govulncheck provides an experimental govulncheck API. +package govulncheck + +import "golang.org/x/vuln/exp/govulncheck" + +var ( + // Source reports vulnerabilities that affect the analyzed packages. + Source = govulncheck.Source + + // DefaultCache constructs cache for a vulnerability database client. + DefaultCache = govulncheck.DefaultCache +) + +type ( + // Config is the configuration for Main. + Config = govulncheck.Config + + // Result is the result of executing Source. + Result = govulncheck.Result + + // Vuln represents a single OSV entry. + Vuln = govulncheck.Vuln + + // Module represents a specific vulnerability relevant to a + // single module or package. + Module = govulncheck.Module + + // Package is a Go package with known vulnerable symbols. + Package = govulncheck.Package + + // CallStacks contains a representative call stack for each + // vulnerable symbol that is called. + CallStack = govulncheck.CallStack + + // StackFrame represents a call stack entry. + StackFrame = govulncheck.StackFrame +) diff --git a/gopls/golang_org_x_tools_gopls/govulncheck/types_not118.go b/gopls/golang_org_x_tools_gopls/govulncheck/types_not118.go new file mode 100644 index 0000000..ae92caa --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/govulncheck/types_not118.go @@ -0,0 +1,133 @@ +// 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 govulncheck + +import ( + "go/token" + + "golang.org/x/vuln/osv" +) + +// Result is the result of executing Source or Binary. +type Result struct { + // Vulns contains all vulnerabilities that are called or imported by + // the analyzed module. + Vulns []*Vuln +} + +// Vuln represents a single OSV entry. +type Vuln struct { + // OSV contains all data from the OSV entry for this vulnerability. + OSV *osv.Entry + + // Modules contains all of the modules in the OSV entry where a + // vulnerable package is imported by the target source code or binary. + // + // For example, a module M with two packages M/p1 and M/p2, where only p1 + // is vulnerable, will appear in this list if and only if p1 is imported by + // the target source code or binary. + Modules []*Module +} + +func (v *Vuln) IsCalled() bool { + return false +} + +// Module represents a specific vulnerability relevant to a single module. +type Module struct { + // Path is the module path of the module containing the vulnerability. + // + // Importable packages in the standard library will have the path "stdlib". + Path string + + // FoundVersion is the module version where the vulnerability was found. + FoundVersion string + + // FixedVersion is the module version where the vulnerability was + // fixed. If there are multiple fixed versions in the OSV report, this will + // be the latest fixed version. + // + // This is empty if a fix is not available. + FixedVersion string + + // Packages contains all the vulnerable packages in OSV entry that are + // imported by the target source code or binary. + // + // For example, given a module M with two packages M/p1 and M/p2, where + // both p1 and p2 are vulnerable, p1 and p2 will each only appear in this + // list they are individually imported by the target source code or binary. + Packages []*Package +} + +// Package is a Go package with known vulnerable symbols. +type Package struct { + // Path is the import path of the package containing the vulnerability. + Path string + + // CallStacks contains a representative call stack for each + // vulnerable symbol that is called. + // + // For vulnerabilities found from binary analysis, only CallStack.Symbol + // will be provided. + // + // For non-affecting vulnerabilities reported from the source mode + // analysis, this will be empty. + CallStacks []CallStack +} + +// CallStacks contains a representative call stack for a vulnerable +// symbol. +type CallStack struct { + // Symbol is the name of the detected vulnerable function + // or method. + // + // This follows the naming convention in the OSV report. + Symbol string + + // Summary is a one-line description of the callstack, used by the + // default govulncheck mode. + // + // Example: module3.main calls github.com/shiyanhui/dht.DHT.Run + Summary string + + // Frames contains an entry for each stack in the call stack. + // + // Frames are sorted starting from the entry point to the + // imported vulnerable symbol. The last frame in Frames should match + // Symbol. + Frames []*StackFrame +} + +// StackFrame represents a call stack entry. +type StackFrame struct { + // PackagePath is the import path. + PkgPath string + + // FuncName is the function name. + FuncName string + + // RecvType is the fully qualified receiver type, + // if the called symbol is a method. + // + // The client can create the final symbol name by + // prepending RecvType to FuncName. + RecvType string + + // Position describes an arbitrary source position + // including the file, line, and column location. + // A Position is valid if the line number is > 0. + Position token.Position +} + +func (sf *StackFrame) Name() string { + return "" +} + +func (sf *StackFrame) Pos() string { + return "" +} diff --git a/gopls/golang_org_x_tools_gopls/govulncheck/util.go b/gopls/golang_org_x_tools_gopls/govulncheck/util.go new file mode 100644 index 0000000..9e56908 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/govulncheck/util.go @@ -0,0 +1,123 @@ +// 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 govulncheck + +import ( + "fmt" + "strings" + + "golang.org/x/mod/semver" + isem "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/govulncheck/semver" + "golang.org/x/vuln/osv" + "golang.org/x/vuln/vulncheck" +) + +// LatestFixed returns the latest fixed version in the list of affected ranges, +// or the empty string if there are no fixed versions. +func LatestFixed(as []osv.Affected) string { + v := "" + for _, a := range as { + for _, r := range a.Ranges { + if r.Type == osv.TypeSemver { + for _, e := range r.Events { + if e.Fixed != "" && (v == "" || + semver.Compare(isem.CanonicalizeSemverPrefix(e.Fixed), isem.CanonicalizeSemverPrefix(v)) > 0) { + v = e.Fixed + } + } + } + } + } + return v +} + +// SummarizeCallStack returns a short description of the call stack. +// It uses one of two forms, depending on what the lowest function F in topPkgs +// calls: +// - If it calls a function V from the vulnerable package, then summarizeCallStack +// returns "F calls V". +// - If it calls a function G in some other package, which eventually calls V, +// it returns "F calls G, which eventually calls V". +// +// If it can't find any of these functions, summarizeCallStack returns the empty string. +func SummarizeCallStack(cs vulncheck.CallStack, topPkgs map[string]bool, vulnPkg string) string { + // Find the lowest function in the top packages. + iTop := lowest(cs, func(e vulncheck.StackEntry) bool { + return topPkgs[PkgPath(e.Function)] + }) + if iTop < 0 { + return "" + } + // Find the highest function in the vulnerable package that is below iTop. + iVuln := highest(cs[iTop+1:], func(e vulncheck.StackEntry) bool { + return PkgPath(e.Function) == vulnPkg + }) + if iVuln < 0 { + return "" + } + iVuln += iTop + 1 // adjust for slice in call to highest. + topName := FuncName(cs[iTop].Function) + topPos := AbsRelShorter(FuncPos(cs[iTop].Call)) + if topPos != "" { + topPos += ": " + } + vulnName := FuncName(cs[iVuln].Function) + if iVuln == iTop+1 { + return fmt.Sprintf("%s%s calls %s", topPos, topName, vulnName) + } + return fmt.Sprintf("%s%s calls %s, which eventually calls %s", + topPos, topName, FuncName(cs[iTop+1].Function), vulnName) +} + +// highest returns the highest (one with the smallest index) entry in the call +// stack for which f returns true. +func highest(cs vulncheck.CallStack, f func(e vulncheck.StackEntry) bool) int { + for i := 0; i < len(cs); i++ { + if f(cs[i]) { + return i + } + } + return -1 +} + +// lowest returns the lowest (one with the largets index) entry in the call +// stack for which f returns true. +func lowest(cs vulncheck.CallStack, f func(e vulncheck.StackEntry) bool) int { + for i := len(cs) - 1; i >= 0; i-- { + if f(cs[i]) { + return i + } + } + return -1 +} + +// PkgPath returns the package path from fn. +func PkgPath(fn *vulncheck.FuncNode) string { + if fn.PkgPath != "" { + return fn.PkgPath + } + s := strings.TrimPrefix(fn.RecvType, "*") + if i := strings.LastIndexByte(s, '.'); i > 0 { + s = s[:i] + } + return s +} + +// FuncName returns the function name from fn, adjusted +// to remove pointer annotations. +func FuncName(fn *vulncheck.FuncNode) string { + return strings.TrimPrefix(fn.String(), "*") +} + +// FuncPos returns the function position from call. +func FuncPos(call *vulncheck.CallSite) string { + if call != nil && call.Pos != nil { + return call.Pos.String() + } + return "" +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/embeddirective/embeddirective.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/embeddirective/embeddirective.go new file mode 100644 index 0000000..1b504f7 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/embeddirective/embeddirective.go @@ -0,0 +1,58 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package embeddirective defines an Analyzer that validates import for //go:embed directive. +package embeddirective + +import ( + "go/ast" + "strings" + + "golang.org/x/tools/go/analysis" +) + +const Doc = `check for //go:embed directive import + +This analyzer checks that the embed package is imported when source code contains //go:embed comment directives. +The embed package must be imported for //go:embed directives to function.import _ "embed".` + +var Analyzer = &analysis.Analyzer{ + Name: "embed", + Doc: Doc, + Requires: []*analysis.Analyzer{}, + Run: run, + RunDespiteErrors: true, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + com := hasEmbedDirectiveComment(f) + if com != nil { + assertEmbedImport(pass, com, f) + } + } + return nil, nil +} + +// Check if the comment contains //go:embed directive. +func hasEmbedDirectiveComment(f *ast.File) *ast.Comment { + for _, cg := range f.Comments { + for _, c := range cg.List { + if strings.HasPrefix(c.Text, "//go:embed ") { + return c + } + } + } + return nil +} + +// Verifies that "embed" import exists for //go:embed directive. +func assertEmbedImport(pass *analysis.Pass, com *ast.Comment, f *ast.File) { + for _, imp := range f.Imports { + if "\"embed\"" == imp.Path.Value { + return + } + } + pass.Report(analysis.Diagnostic{Pos: com.Pos(), End: com.Pos() + 10, Message: "The \"embed\" package must be imported when using go:embed directives."}) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/fillreturns/fillreturns.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/fillreturns/fillreturns.go new file mode 100644 index 0000000..de74ed8 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/fillreturns/fillreturns.go @@ -0,0 +1,280 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fillreturns defines an Analyzer that will attempt to +// automatically fill in a return statement that has missing +// values with zero value elements. +package fillreturns + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/types" + "regexp" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/fuzzy" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +const Doc = `suggest fixes for errors due to an incorrect number of return values + +This checker provides suggested fixes for type errors of the +type "wrong number of return values (want %d, got %d)". For example: + func m() (int, string, *bool, error) { + return + } +will turn into + func m() (int, string, *bool, error) { + return 0, "", nil, nil + } + +This functionality is similar to https://github.com/sqs/goreturns. +` + +var Analyzer = &analysis.Analyzer{ + Name: "fillreturns", + Doc: Doc, + Requires: []*analysis.Analyzer{}, + Run: run, + RunDespiteErrors: true, +} + +func run(pass *analysis.Pass) (interface{}, error) { + info := pass.TypesInfo + if info == nil { + return nil, fmt.Errorf("nil TypeInfo") + } + + errors := analysisinternal.GetTypeErrors(pass) +outer: + for _, typeErr := range errors { + // Filter out the errors that are not relevant to this analyzer. + if !FixesError(typeErr) { + continue + } + var file *ast.File + for _, f := range pass.Files { + if f.Pos() <= typeErr.Pos && typeErr.Pos <= f.End() { + file = f + break + } + } + if file == nil { + continue + } + + // Get the end position of the error. + // (This heuristic assumes that the buffer is formatted, + // at least up to the end position of the error.) + var buf bytes.Buffer + if err := format.Node(&buf, pass.Fset, file); err != nil { + continue + } + typeErrEndPos := analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), typeErr.Pos) + + // TODO(rfindley): much of the error handling code below returns, when it + // should probably continue. + + // Get the path for the relevant range. + path, _ := astutil.PathEnclosingInterval(file, typeErr.Pos, typeErrEndPos) + if len(path) == 0 { + return nil, nil + } + + // Find the enclosing return statement. + var ret *ast.ReturnStmt + var retIdx int + for i, n := range path { + if r, ok := n.(*ast.ReturnStmt); ok { + ret = r + retIdx = i + break + } + } + if ret == nil { + return nil, nil + } + + // Get the function type that encloses the ReturnStmt. + var enclosingFunc *ast.FuncType + for _, n := range path[retIdx+1:] { + switch node := n.(type) { + case *ast.FuncLit: + enclosingFunc = node.Type + case *ast.FuncDecl: + enclosingFunc = node.Type + } + if enclosingFunc != nil { + break + } + } + if enclosingFunc == nil || enclosingFunc.Results == nil { + continue + } + + // Skip any generic enclosing functions, since type parameters don't + // have 0 values. + // TODO(rfindley): We should be able to handle this if the return + // values are all concrete types. + if tparams := typeparams.ForFuncType(enclosingFunc); tparams != nil && tparams.NumFields() > 0 { + return nil, nil + } + + // Find the function declaration that encloses the ReturnStmt. + var outer *ast.FuncDecl + for _, p := range path { + if p, ok := p.(*ast.FuncDecl); ok { + outer = p + break + } + } + if outer == nil { + return nil, nil + } + + // Skip any return statements that contain function calls with multiple + // return values. + for _, expr := range ret.Results { + e, ok := expr.(*ast.CallExpr) + if !ok { + continue + } + if tup, ok := info.TypeOf(e).(*types.Tuple); ok && tup.Len() > 1 { + continue outer + } + } + + // Duplicate the return values to track which values have been matched. + remaining := make([]ast.Expr, len(ret.Results)) + copy(remaining, ret.Results) + + fixed := make([]ast.Expr, len(enclosingFunc.Results.List)) + + // For each value in the return function declaration, find the leftmost element + // in the return statement that has the desired type. If no such element exists, + // fill in the missing value with the appropriate "zero" value. + // Beware that type information may be incomplete. + var retTyps []types.Type + for _, ret := range enclosingFunc.Results.List { + retTyp := info.TypeOf(ret.Type) + if retTyp == nil { + return nil, nil + } + retTyps = append(retTyps, retTyp) + } + matches := analysisinternal.MatchingIdents(retTyps, file, ret.Pos(), info, pass.Pkg) + for i, retTyp := range retTyps { + var match ast.Expr + var idx int + for j, val := range remaining { + if t := info.TypeOf(val); t == nil || !matchingTypes(t, retTyp) { + continue + } + if !analysisinternal.IsZeroValue(val) { + match, idx = val, j + break + } + // If the current match is a "zero" value, we keep searching in + // case we find a non-"zero" value match. If we do not find a + // non-"zero" value, we will use the "zero" value. + match, idx = val, j + } + + if match != nil { + fixed[i] = match + remaining = append(remaining[:idx], remaining[idx+1:]...) + } else { + names, ok := matches[retTyp] + if !ok { + return nil, fmt.Errorf("invalid return type: %v", retTyp) + } + // Find the identifier most similar to the return type. + // If no identifier matches the pattern, generate a zero value. + if best := fuzzy.BestMatch(retTyp.String(), names); best != "" { + fixed[i] = ast.NewIdent(best) + } else if zero := analysisinternal.ZeroValue(file, pass.Pkg, retTyp); zero != nil { + fixed[i] = zero + } else { + return nil, nil + } + } + } + + // Remove any non-matching "zero values" from the leftover values. + var nonZeroRemaining []ast.Expr + for _, expr := range remaining { + if !analysisinternal.IsZeroValue(expr) { + nonZeroRemaining = append(nonZeroRemaining, expr) + } + } + // Append leftover return values to end of new return statement. + fixed = append(fixed, nonZeroRemaining...) + + newRet := &ast.ReturnStmt{ + Return: ret.Pos(), + Results: fixed, + } + + // Convert the new return statement AST to text. + var newBuf bytes.Buffer + if err := format.Node(&newBuf, pass.Fset, newRet); err != nil { + return nil, err + } + + pass.Report(analysis.Diagnostic{ + Pos: typeErr.Pos, + End: typeErrEndPos, + Message: typeErr.Msg, + SuggestedFixes: []analysis.SuggestedFix{{ + Message: "Fill in return values", + TextEdits: []analysis.TextEdit{{ + Pos: ret.Pos(), + End: ret.End(), + NewText: newBuf.Bytes(), + }}, + }}, + }) + } + return nil, nil +} + +func matchingTypes(want, got types.Type) bool { + if want == got || types.Identical(want, got) { + return true + } + // Code segment to help check for untyped equality from (golang/go#32146). + if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 { + if lhs, ok := got.Underlying().(*types.Basic); ok { + return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType + } + } + return types.AssignableTo(want, got) || types.ConvertibleTo(want, got) +} + +// Error messages have changed across Go versions. These regexps capture recent +// incarnations. +// +// TODO(rfindley): once error codes are exported and exposed via go/packages, +// use error codes rather than string matching here. +var wrongReturnNumRegexes = []*regexp.Regexp{ + regexp.MustCompile(`wrong number of return values \(want (\d+), got (\d+)\)`), + regexp.MustCompile(`too many return values`), + regexp.MustCompile(`not enough return values`), +} + +func FixesError(err types.Error) bool { + msg := strings.TrimSpace(err.Msg) + for _, rx := range wrongReturnNumRegexes { + if rx.MatchString(msg) { + return true + } + } + return false +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/fillstruct/fillstruct.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/fillstruct/fillstruct.go new file mode 100644 index 0000000..96603e6 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/fillstruct/fillstruct.go @@ -0,0 +1,506 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fillstruct defines an Analyzer that automatically +// fills in a struct declaration with zero value elements for each field. +// +// The analyzer's diagnostic is merely a prompt. +// The actual fix is created by a separate direct call from gopls to +// the SuggestedFixes function. +// Tests of Analyzer.Run can be found in ./testdata/src. +// Tests of the SuggestedFixes logic live in ../../testdata/fillstruct. +package fillstruct + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/token" + "go/types" + "strings" + "unicode" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/go/ast/inspector" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/fuzzy" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +const Doc = `note incomplete struct initializations + +This analyzer provides diagnostics for any struct literals that do not have +any fields initialized. Because the suggested fix for this analysis is +expensive to compute, callers should compute it separately, using the +SuggestedFix function below. +` + +var Analyzer = &analysis.Analyzer{ + Name: "fillstruct", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, + RunDespiteErrors: true, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)} + inspect.Preorder(nodeFilter, func(n ast.Node) { + expr := n.(*ast.CompositeLit) + + // Find enclosing file. + // TODO(adonovan): use inspect.WithStack? + var file *ast.File + for _, f := range pass.Files { + if f.Pos() <= expr.Pos() && expr.Pos() <= f.End() { + file = f + break + } + } + if file == nil { + return + } + + typ := pass.TypesInfo.TypeOf(expr) + if typ == nil { + return + } + + // Find reference to the type declaration of the struct being initialized. + typ = deref(typ) + tStruct, ok := typ.Underlying().(*types.Struct) + if !ok { + return + } + // Inv: typ is the possibly-named struct type. + + fieldCount := tStruct.NumFields() + + // Skip any struct that is already populated or that has no fields. + if fieldCount == 0 || fieldCount == len(expr.Elts) { + return + } + + // Are any fields in need of filling? + var fillableFields []string + for i := 0; i < fieldCount; i++ { + field := tStruct.Field(i) + // Ignore fields that are not accessible in the current package. + if field.Pkg() != nil && field.Pkg() != pass.Pkg && !field.Exported() { + continue + } + fillableFields = append(fillableFields, fmt.Sprintf("%s: %s", field.Name(), field.Type().String())) + } + if len(fillableFields) == 0 { + return + } + + // Derive a name for the struct type. + var name string + if typ != tStruct { + // named struct type (e.g. pkg.S[T]) + name = types.TypeString(typ, types.RelativeTo(pass.Pkg)) + } else { + // anonymous struct type + totalFields := len(fillableFields) + const maxLen = 20 + // Find the index to cut off printing of fields. + var i, fieldLen int + for i = range fillableFields { + if fieldLen > maxLen { + break + } + fieldLen += len(fillableFields[i]) + } + fillableFields = fillableFields[:i] + if i < totalFields { + fillableFields = append(fillableFields, "...") + } + name = fmt.Sprintf("anonymous struct { %s }", strings.Join(fillableFields, ", ")) + } + pass.Report(analysis.Diagnostic{ + Message: fmt.Sprintf("Fill %s", name), + Pos: expr.Pos(), + End: expr.End(), + }) + }) + return nil, nil +} + +// SuggestedFix computes the suggested fix for the kinds of +// diagnostics produced by the Analyzer above. +func SuggestedFix(fset *token.FileSet, rng span.Range, content []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) { + if info == nil { + return nil, fmt.Errorf("nil types.Info") + } + + pos := rng.Start // don't use the end + + // TODO(rstambler): Using ast.Inspect would probably be more efficient than + // calling PathEnclosingInterval. Switch this approach. + path, _ := astutil.PathEnclosingInterval(file, pos, pos) + if len(path) == 0 { + return nil, fmt.Errorf("no enclosing ast.Node") + } + var expr *ast.CompositeLit + for _, n := range path { + if node, ok := n.(*ast.CompositeLit); ok { + expr = node + break + } + } + + typ := info.TypeOf(expr) + if typ == nil { + return nil, fmt.Errorf("no composite literal") + } + + // Find reference to the type declaration of the struct being initialized. + typ = deref(typ) + tStruct, ok := typ.Underlying().(*types.Struct) + if !ok { + return nil, fmt.Errorf("%s is not a (pointer to) struct type", + types.TypeString(typ, types.RelativeTo(pkg))) + } + // Inv: typ is the the possibly-named struct type. + + fieldCount := tStruct.NumFields() + + // Check which types have already been filled in. (we only want to fill in + // the unfilled types, or else we'll blat user-supplied details) + prefilledFields := map[string]ast.Expr{} + for _, e := range expr.Elts { + if kv, ok := e.(*ast.KeyValueExpr); ok { + if key, ok := kv.Key.(*ast.Ident); ok { + prefilledFields[key.Name] = kv.Value + } + } + } + + // Use a new fileset to build up a token.File for the new composite + // literal. We need one line for foo{, one line for }, and one line for + // each field we're going to set. format.Node only cares about line + // numbers, so we don't need to set columns, and each line can be + // 1 byte long. + // TODO(adonovan): why is this necessary? The position information + // is going to be wrong for the existing trees in prefilledFields. + // Can't the formatter just do its best with an empty fileset? + fakeFset := token.NewFileSet() + tok := fakeFset.AddFile("", -1, fieldCount+2) + + line := 2 // account for 1-based lines and the left brace + var fieldTyps []types.Type + for i := 0; i < fieldCount; i++ { + field := tStruct.Field(i) + // Ignore fields that are not accessible in the current package. + if field.Pkg() != nil && field.Pkg() != pkg && !field.Exported() { + fieldTyps = append(fieldTyps, nil) + continue + } + fieldTyps = append(fieldTyps, field.Type()) + } + matches := analysisinternal.MatchingIdents(fieldTyps, file, rng.Start, info, pkg) + var elts []ast.Expr + for i, fieldTyp := range fieldTyps { + if fieldTyp == nil { + continue // TODO(adonovan): is this reachable? + } + fieldName := tStruct.Field(i).Name() + + tok.AddLine(line - 1) // add 1 byte per line + if line > tok.LineCount() { + panic(fmt.Sprintf("invalid line number %v (of %v) for fillstruct", line, tok.LineCount())) + } + pos := tok.LineStart(line) + + kv := &ast.KeyValueExpr{ + Key: &ast.Ident{ + NamePos: pos, + Name: fieldName, + }, + Colon: pos, + } + if expr, ok := prefilledFields[fieldName]; ok { + kv.Value = expr + } else { + names, ok := matches[fieldTyp] + if !ok { + return nil, fmt.Errorf("invalid struct field type: %v", fieldTyp) + } + + // Find the name most similar to the field name. + // If no name matches the pattern, generate a zero value. + // NOTE: We currently match on the name of the field key rather than the field type. + if best := fuzzy.BestMatch(fieldName, names); best != "" { + kv.Value = ast.NewIdent(best) + } else if v := populateValue(file, pkg, fieldTyp); v != nil { + kv.Value = v + } else { + return nil, nil + } + } + elts = append(elts, kv) + line++ + } + + // If all of the struct's fields are unexported, we have nothing to do. + if len(elts) == 0 { + return nil, fmt.Errorf("no elements to fill") + } + + // Add the final line for the right brace. Offset is the number of + // bytes already added plus 1. + tok.AddLine(len(elts) + 1) + line = len(elts) + 2 + if line > tok.LineCount() { + panic(fmt.Sprintf("invalid line number %v (of %v) for fillstruct", line, tok.LineCount())) + } + + cl := &ast.CompositeLit{ + Type: expr.Type, + Lbrace: tok.LineStart(1), + Elts: elts, + Rbrace: tok.LineStart(line), + } + + // Find the line on which the composite literal is declared. + split := bytes.Split(content, []byte("\n")) + lineNumber := fset.Position(expr.Lbrace).Line + firstLine := split[lineNumber-1] // lines are 1-indexed + + // Trim the whitespace from the left of the line, and use the index + // to get the amount of whitespace on the left. + trimmed := bytes.TrimLeftFunc(firstLine, unicode.IsSpace) + index := bytes.Index(firstLine, trimmed) + whitespace := firstLine[:index] + + // First pass through the formatter: turn the expr into a string. + var formatBuf bytes.Buffer + if err := format.Node(&formatBuf, fakeFset, cl); err != nil { + return nil, fmt.Errorf("failed to run first format on:\n%s\ngot err: %v", cl.Type, err) + } + sug := indent(formatBuf.Bytes(), whitespace) + + if len(prefilledFields) > 0 { + // Attempt a second pass through the formatter to line up columns. + sourced, err := format.Source(sug) + if err == nil { + sug = indent(sourced, whitespace) + } + } + + return &analysis.SuggestedFix{ + TextEdits: []analysis.TextEdit{ + { + Pos: expr.Pos(), + End: expr.End(), + NewText: sug, + }, + }, + }, nil +} + +// indent works line by line through str, indenting (prefixing) each line with +// ind. +func indent(str, ind []byte) []byte { + split := bytes.Split(str, []byte("\n")) + newText := bytes.NewBuffer(nil) + for i, s := range split { + if len(s) == 0 { + continue + } + // Don't add the extra indentation to the first line. + if i != 0 { + newText.Write(ind) + } + newText.Write(s) + if i < len(split)-1 { + newText.WriteByte('\n') + } + } + return newText.Bytes() +} + +// populateValue constructs an expression to fill the value of a struct field. +// +// When the type of a struct field is a basic literal or interface, we return +// default values. For other types, such as maps, slices, and channels, we create +// empty expressions such as []T{} or make(chan T) rather than using default values. +// +// The reasoning here is that users will call fillstruct with the intention of +// initializing the struct, in which case setting these fields to nil has no effect. +func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + switch u := typ.Underlying().(type) { + case *types.Basic: + switch { + case u.Info()&types.IsNumeric != 0: + return &ast.BasicLit{Kind: token.INT, Value: "0"} + case u.Info()&types.IsBoolean != 0: + return &ast.Ident{Name: "false"} + case u.Info()&types.IsString != 0: + return &ast.BasicLit{Kind: token.STRING, Value: `""`} + case u.Kind() == types.UnsafePointer: + return ast.NewIdent("nil") + default: + panic("unknown basic type") + } + + case *types.Map: + k := analysisinternal.TypeExpr(f, pkg, u.Key()) + v := analysisinternal.TypeExpr(f, pkg, u.Elem()) + if k == nil || v == nil { + return nil + } + return &ast.CompositeLit{ + Type: &ast.MapType{ + Key: k, + Value: v, + }, + } + case *types.Slice: + s := analysisinternal.TypeExpr(f, pkg, u.Elem()) + if s == nil { + return nil + } + return &ast.CompositeLit{ + Type: &ast.ArrayType{ + Elt: s, + }, + } + + case *types.Array: + a := analysisinternal.TypeExpr(f, pkg, u.Elem()) + if a == nil { + return nil + } + return &ast.CompositeLit{ + Type: &ast.ArrayType{ + Elt: a, + Len: &ast.BasicLit{ + Kind: token.INT, Value: fmt.Sprintf("%v", u.Len()), + }, + }, + } + + case *types.Chan: + v := analysisinternal.TypeExpr(f, pkg, u.Elem()) + if v == nil { + return nil + } + dir := ast.ChanDir(u.Dir()) + if u.Dir() == types.SendRecv { + dir = ast.SEND | ast.RECV + } + return &ast.CallExpr{ + Fun: ast.NewIdent("make"), + Args: []ast.Expr{ + &ast.ChanType{ + Dir: dir, + Value: v, + }, + }, + } + + case *types.Struct: + s := analysisinternal.TypeExpr(f, pkg, typ) + if s == nil { + return nil + } + return &ast.CompositeLit{ + Type: s, + } + + case *types.Signature: + var params []*ast.Field + for i := 0; i < u.Params().Len(); i++ { + p := analysisinternal.TypeExpr(f, pkg, u.Params().At(i).Type()) + if p == nil { + return nil + } + params = append(params, &ast.Field{ + Type: p, + Names: []*ast.Ident{ + { + Name: u.Params().At(i).Name(), + }, + }, + }) + } + var returns []*ast.Field + for i := 0; i < u.Results().Len(); i++ { + r := analysisinternal.TypeExpr(f, pkg, u.Results().At(i).Type()) + if r == nil { + return nil + } + returns = append(returns, &ast.Field{ + Type: r, + }) + } + return &ast.FuncLit{ + Type: &ast.FuncType{ + Params: &ast.FieldList{ + List: params, + }, + Results: &ast.FieldList{ + List: returns, + }, + }, + Body: &ast.BlockStmt{}, + } + + case *types.Pointer: + switch u.Elem().(type) { + case *types.Basic: + return &ast.CallExpr{ + Fun: &ast.Ident{ + Name: "new", + }, + Args: []ast.Expr{ + &ast.Ident{ + Name: u.Elem().String(), + }, + }, + } + default: + return &ast.UnaryExpr{ + Op: token.AND, + X: populateValue(f, pkg, u.Elem()), + } + } + + case *types.Interface: + if param, ok := typ.(*typeparams.TypeParam); ok { + // *new(T) is the zero value of a type parameter T. + // TODO(adonovan): one could give a more specific zero + // value if the type has a core type that is, say, + // always a number or a pointer. See go/ssa for details. + return &ast.StarExpr{ + X: &ast.CallExpr{ + Fun: ast.NewIdent("new"), + Args: []ast.Expr{ + ast.NewIdent(param.Obj().Name()), + }, + }, + } + } + + return ast.NewIdent("nil") + } + return nil +} + +func deref(t types.Type) types.Type { + for { + ptr, ok := t.Underlying().(*types.Pointer) + if !ok { + return t + } + t = ptr.Elem() + } +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/infertypeargs.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/infertypeargs.go new file mode 100644 index 0000000..119de50 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/infertypeargs.go @@ -0,0 +1,31 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package infertypeargs defines an analyzer that checks for explicit function +// arguments that could be inferred. +package infertypeargs + +import ( + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +const Doc = `check for unnecessary type arguments in call expressions + +Explicit type arguments may be omitted from call expressions if they can be +inferred from function arguments, or from other type arguments: + + func f[T any](T) {} + + func _() { + f[string]("foo") // string could be inferred + } +` + +var Analyzer = &analysis.Analyzer{ + Name: "infertypeargs", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/run_go117.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/run_go117.go new file mode 100644 index 0000000..bc5c29b --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/run_go117.go @@ -0,0 +1,16 @@ +// 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 infertypeargs + +import "golang.org/x/tools/go/analysis" + +// This analyzer only relates to go1.18+, and uses the types.CheckExpr API that +// was added in Go 1.13. +func run(pass *analysis.Pass) (interface{}, error) { + return nil, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/run_go118.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/run_go118.go new file mode 100644 index 0000000..c9c021c --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs/run_go118.go @@ -0,0 +1,111 @@ +// 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 infertypeargs + +import ( + "go/ast" + "go/token" + "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.CallExpr)(nil), + } + + inspect.Preorder(nodeFilter, func(node ast.Node) { + call := node.(*ast.CallExpr) + x, lbrack, indices, rbrack := typeparams.UnpackIndexExpr(call.Fun) + ident := calledIdent(x) + if ident == nil || len(indices) == 0 { + return // no explicit args, nothing to do + } + + // Confirm that instantiation actually occurred at this ident. + idata, ok := typeparams.GetInstances(pass.TypesInfo)[ident] + if !ok { + return // something went wrong, but fail open + } + instance := idata.Type + + // Start removing argument expressions from the right, and check if we can + // still infer the call expression. + required := len(indices) // number of type expressions that are required + for i := len(indices) - 1; i >= 0; i-- { + var fun ast.Expr + if i == 0 { + // No longer an index expression: just use the parameterized operand. + fun = x + } else { + fun = typeparams.PackIndexExpr(x, lbrack, indices[:i], indices[i-1].End()) + } + newCall := &ast.CallExpr{ + Fun: fun, + Lparen: call.Lparen, + Args: call.Args, + Ellipsis: call.Ellipsis, + Rparen: call.Rparen, + } + info := new(types.Info) + typeparams.InitInstanceInfo(info) + if err := types.CheckExpr(pass.Fset, pass.Pkg, call.Pos(), newCall, info); err != nil { + // Most likely inference failed. + break + } + newIData := typeparams.GetInstances(info)[ident] + newInstance := newIData.Type + if !types.Identical(instance, newInstance) { + // The inferred result type does not match the original result type, so + // this simplification is not valid. + break + } + required = i + } + if required < len(indices) { + var start, end token.Pos + var edit analysis.TextEdit + if required == 0 { + start, end = lbrack, rbrack+1 // erase the entire index + edit = analysis.TextEdit{Pos: start, End: end} + } else { + start = indices[required].Pos() + end = rbrack + // erase from end of last arg to include last comma & white-spaces + edit = analysis.TextEdit{Pos: indices[required-1].End(), End: end} + } + pass.Report(analysis.Diagnostic{ + Pos: start, + End: end, + Message: "unnecessary type arguments", + SuggestedFixes: []analysis.SuggestedFix{{ + Message: "simplify type arguments", + TextEdits: []analysis.TextEdit{edit}, + }}, + }) + } + }) + + return nil, nil +} + +func calledIdent(x ast.Expr) *ast.Ident { + switch x := x.(type) { + case *ast.Ident: + return x + case *ast.SelectorExpr: + return x.Sel + } + return nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/nonewvars/nonewvars.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/nonewvars/nonewvars.go new file mode 100644 index 0000000..a9aecc1 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/nonewvars/nonewvars.go @@ -0,0 +1,93 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package nonewvars defines an Analyzer that applies suggested fixes +// to errors of the type "no new variables on left side of :=". +package nonewvars + +import ( + "bytes" + "go/ast" + "go/format" + "go/token" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" +) + +const Doc = `suggested fixes for "no new vars on left side of :=" + +This checker provides suggested fixes for type errors of the +type "no new vars on left side of :=". For example: + z := 1 + z := 2 +will turn into + z := 1 + z = 2 +` + +var Analyzer = &analysis.Analyzer{ + Name: string(analysisinternal.NoNewVars), + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, + RunDespiteErrors: true, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + errors := analysisinternal.GetTypeErrors(pass) + + nodeFilter := []ast.Node{(*ast.AssignStmt)(nil)} + inspect.Preorder(nodeFilter, func(n ast.Node) { + assignStmt, _ := n.(*ast.AssignStmt) + // We only care about ":=". + if assignStmt.Tok != token.DEFINE { + return + } + + var file *ast.File + for _, f := range pass.Files { + if f.Pos() <= assignStmt.Pos() && assignStmt.Pos() < f.End() { + file = f + break + } + } + if file == nil { + return + } + + for _, err := range errors { + if !FixesError(err.Msg) { + continue + } + if assignStmt.Pos() > err.Pos || err.Pos >= assignStmt.End() { + continue + } + var buf bytes.Buffer + if err := format.Node(&buf, pass.Fset, file); err != nil { + continue + } + pass.Report(analysis.Diagnostic{ + Pos: err.Pos, + End: analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos), + Message: err.Msg, + SuggestedFixes: []analysis.SuggestedFix{{ + Message: "Change ':=' to '='", + TextEdits: []analysis.TextEdit{{ + Pos: err.Pos, + End: err.Pos + 1, + }}, + }}, + }) + } + }) + return nil, nil +} + +func FixesError(msg string) bool { + return msg == "no new variables on left side of :=" +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/noresultvalues/noresultvalues.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/noresultvalues/noresultvalues.go new file mode 100644 index 0000000..2420bab --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/noresultvalues/noresultvalues.go @@ -0,0 +1,90 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package noresultvalues defines an Analyzer that applies suggested fixes +// to errors of the type "no result values expected". +package noresultvalues + +import ( + "bytes" + "go/ast" + "go/format" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" +) + +const Doc = `suggested fixes for unexpected return values + +This checker provides suggested fixes for type errors of the +type "no result values expected" or "too many return values". +For example: + func z() { return nil } +will turn into + func z() { return } +` + +var Analyzer = &analysis.Analyzer{ + Name: string(analysisinternal.NoResultValues), + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, + RunDespiteErrors: true, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + errors := analysisinternal.GetTypeErrors(pass) + + nodeFilter := []ast.Node{(*ast.ReturnStmt)(nil)} + inspect.Preorder(nodeFilter, func(n ast.Node) { + retStmt, _ := n.(*ast.ReturnStmt) + + var file *ast.File + for _, f := range pass.Files { + if f.Pos() <= retStmt.Pos() && retStmt.Pos() < f.End() { + file = f + break + } + } + if file == nil { + return + } + + for _, err := range errors { + if !FixesError(err.Msg) { + continue + } + if retStmt.Pos() >= err.Pos || err.Pos >= retStmt.End() { + continue + } + var buf bytes.Buffer + if err := format.Node(&buf, pass.Fset, file); err != nil { + continue + } + pass.Report(analysis.Diagnostic{ + Pos: err.Pos, + End: analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos), + Message: err.Msg, + SuggestedFixes: []analysis.SuggestedFix{{ + Message: "Delete return values", + TextEdits: []analysis.TextEdit{{ + Pos: retStmt.Pos(), + End: retStmt.End(), + NewText: []byte("return"), + }}, + }}, + }) + } + }) + return nil, nil +} + +func FixesError(msg string) bool { + return msg == "no result values expected" || + strings.HasPrefix(msg, "too many return values") && strings.Contains(msg, "want ()") +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifycompositelit/simplifycompositelit.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifycompositelit/simplifycompositelit.go new file mode 100644 index 0000000..c91fc75 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifycompositelit/simplifycompositelit.go @@ -0,0 +1,196 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package simplifycompositelit defines an Analyzer that simplifies composite literals. +// https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go +// https://golang.org/cmd/gofmt/#hdr-The_simplify_command +package simplifycompositelit + +import ( + "bytes" + "fmt" + "go/ast" + "go/printer" + "go/token" + "reflect" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const Doc = `check for composite literal simplifications + +An array, slice, or map composite literal of the form: + []T{T{}, T{}} +will be simplified to: + []T{{}, {}} + +This is one of the simplifications that "gofmt -s" applies.` + +var Analyzer = &analysis.Analyzer{ + Name: "simplifycompositelit", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)} + inspect.Preorder(nodeFilter, func(n ast.Node) { + expr := n.(*ast.CompositeLit) + + outer := expr + var keyType, eltType ast.Expr + switch typ := outer.Type.(type) { + case *ast.ArrayType: + eltType = typ.Elt + case *ast.MapType: + keyType = typ.Key + eltType = typ.Value + } + + if eltType == nil { + return + } + var ktyp reflect.Value + if keyType != nil { + ktyp = reflect.ValueOf(keyType) + } + typ := reflect.ValueOf(eltType) + for _, x := range outer.Elts { + // look at value of indexed/named elements + if t, ok := x.(*ast.KeyValueExpr); ok { + if keyType != nil { + simplifyLiteral(pass, ktyp, keyType, t.Key) + } + x = t.Value + } + simplifyLiteral(pass, typ, eltType, x) + } + }) + return nil, nil +} + +func simplifyLiteral(pass *analysis.Pass, typ reflect.Value, astType, x ast.Expr) { + // if the element is a composite literal and its literal type + // matches the outer literal's element type exactly, the inner + // literal type may be omitted + if inner, ok := x.(*ast.CompositeLit); ok && match(typ, reflect.ValueOf(inner.Type)) { + var b bytes.Buffer + printer.Fprint(&b, pass.Fset, inner.Type) + createDiagnostic(pass, inner.Type.Pos(), inner.Type.End(), b.String()) + } + // if the outer literal's element type is a pointer type *T + // and the element is & of a composite literal of type T, + // the inner &T may be omitted. + if ptr, ok := astType.(*ast.StarExpr); ok { + if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND { + if inner, ok := addr.X.(*ast.CompositeLit); ok { + if match(reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) { + var b bytes.Buffer + printer.Fprint(&b, pass.Fset, inner.Type) + // Account for the & by subtracting 1 from typ.Pos(). + createDiagnostic(pass, inner.Type.Pos()-1, inner.Type.End(), "&"+b.String()) + } + } + } + } +} + +func createDiagnostic(pass *analysis.Pass, start, end token.Pos, typ string) { + pass.Report(analysis.Diagnostic{ + Pos: start, + End: end, + Message: "redundant type from array, slice, or map composite literal", + SuggestedFixes: []analysis.SuggestedFix{{ + Message: fmt.Sprintf("Remove '%s'", typ), + TextEdits: []analysis.TextEdit{{ + Pos: start, + End: end, + NewText: []byte{}, + }}, + }}, + }) +} + +// match reports whether pattern matches val, +// recording wildcard submatches in m. +// If m == nil, match checks whether pattern == val. +// from https://github.com/golang/go/blob/26154f31ad6c801d8bad5ef58df1e9263c6beec7/src/cmd/gofmt/rewrite.go#L160 +func match(pattern, val reflect.Value) bool { + // Otherwise, pattern and val must match recursively. + if !pattern.IsValid() || !val.IsValid() { + return !pattern.IsValid() && !val.IsValid() + } + if pattern.Type() != val.Type() { + return false + } + + // Special cases. + switch pattern.Type() { + case identType: + // For identifiers, only the names need to match + // (and none of the other *ast.Object information). + // This is a common case, handle it all here instead + // of recursing down any further via reflection. + p := pattern.Interface().(*ast.Ident) + v := val.Interface().(*ast.Ident) + return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name + case objectPtrType, positionType: + // object pointers and token positions always match + return true + case callExprType: + // For calls, the Ellipsis fields (token.Position) must + // match since that is how f(x) and f(x...) are different. + // Check them here but fall through for the remaining fields. + p := pattern.Interface().(*ast.CallExpr) + v := val.Interface().(*ast.CallExpr) + if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() { + return false + } + } + + p := reflect.Indirect(pattern) + v := reflect.Indirect(val) + if !p.IsValid() || !v.IsValid() { + return !p.IsValid() && !v.IsValid() + } + + switch p.Kind() { + case reflect.Slice: + if p.Len() != v.Len() { + return false + } + for i := 0; i < p.Len(); i++ { + if !match(p.Index(i), v.Index(i)) { + return false + } + } + return true + + case reflect.Struct: + for i := 0; i < p.NumField(); i++ { + if !match(p.Field(i), v.Field(i)) { + return false + } + } + return true + + case reflect.Interface: + return match(p.Elem(), v.Elem()) + } + + // Handle token integers, etc. + return p.Interface() == v.Interface() +} + +// Values/types for special cases. +var ( + identType = reflect.TypeOf((*ast.Ident)(nil)) + objectPtrType = reflect.TypeOf((*ast.Object)(nil)) + positionType = reflect.TypeOf(token.NoPos) + callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) +) diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyrange/simplifyrange.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyrange/simplifyrange.go new file mode 100644 index 0000000..c9cb387 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyrange/simplifyrange.go @@ -0,0 +1,116 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package simplifyrange defines an Analyzer that simplifies range statements. +// https://golang.org/cmd/gofmt/#hdr-The_simplify_command +// https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go +package simplifyrange + +import ( + "bytes" + "go/ast" + "go/printer" + "go/token" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const Doc = `check for range statement simplifications + +A range of the form: + for x, _ = range v {...} +will be simplified to: + for x = range v {...} + +A range of the form: + for _ = range v {...} +will be simplified to: + for range v {...} + +This is one of the simplifications that "gofmt -s" applies.` + +var Analyzer = &analysis.Analyzer{ + Name: "simplifyrange", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.RangeStmt)(nil), + } + inspect.Preorder(nodeFilter, func(n ast.Node) { + var copy *ast.RangeStmt + if stmt, ok := n.(*ast.RangeStmt); ok { + x := *stmt + copy = &x + } + if copy == nil { + return + } + end := newlineIndex(pass.Fset, copy) + + // Range statements of the form: for i, _ := range x {} + var old ast.Expr + if isBlank(copy.Value) { + old = copy.Value + copy.Value = nil + } + // Range statements of the form: for _ := range x {} + if isBlank(copy.Key) && copy.Value == nil { + old = copy.Key + copy.Key = nil + } + // Return early if neither if condition is met. + if old == nil { + return + } + pass.Report(analysis.Diagnostic{ + Pos: old.Pos(), + End: old.End(), + Message: "simplify range expression", + SuggestedFixes: suggestedFixes(pass.Fset, copy, end), + }) + }) + return nil, nil +} + +func suggestedFixes(fset *token.FileSet, rng *ast.RangeStmt, end token.Pos) []analysis.SuggestedFix { + var b bytes.Buffer + printer.Fprint(&b, fset, rng) + stmt := b.Bytes() + index := bytes.Index(stmt, []byte("\n")) + // If there is a new line character, then don't replace the body. + if index != -1 { + stmt = stmt[:index] + } + return []analysis.SuggestedFix{{ + Message: "Remove empty value", + TextEdits: []analysis.TextEdit{{ + Pos: rng.Pos(), + End: end, + NewText: stmt[:index], + }}, + }} +} + +func newlineIndex(fset *token.FileSet, rng *ast.RangeStmt) token.Pos { + var b bytes.Buffer + printer.Fprint(&b, fset, rng) + contents := b.Bytes() + index := bytes.Index(contents, []byte("\n")) + if index == -1 { + return rng.End() + } + return rng.Pos() + token.Pos(index) +} + +func isBlank(x ast.Expr) bool { + ident, ok := x.(*ast.Ident) + return ok && ident.Name == "_" +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyslice/simplifyslice.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyslice/simplifyslice.go new file mode 100644 index 0000000..da1728e --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyslice/simplifyslice.go @@ -0,0 +1,94 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package simplifyslice defines an Analyzer that simplifies slice statements. +// https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go +// https://golang.org/cmd/gofmt/#hdr-The_simplify_command +package simplifyslice + +import ( + "bytes" + "fmt" + "go/ast" + "go/printer" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const Doc = `check for slice simplifications + +A slice expression of the form: + s[a:len(s)] +will be simplified to: + s[a:] + +This is one of the simplifications that "gofmt -s" applies.` + +var Analyzer = &analysis.Analyzer{ + Name: "simplifyslice", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +// Note: We could also simplify slice expressions of the form s[0:b] to s[:b] +// but we leave them as is since sometimes we want to be very explicit +// about the lower bound. +// An example where the 0 helps: +// x, y, z := b[0:2], b[2:4], b[4:6] +// An example where it does not: +// x, y := b[:n], b[n:] + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.SliceExpr)(nil), + } + inspect.Preorder(nodeFilter, func(n ast.Node) { + expr := n.(*ast.SliceExpr) + // - 3-index slices always require the 2nd and 3rd index + if expr.Max != nil { + return + } + s, ok := expr.X.(*ast.Ident) + // the array/slice object is a single, resolved identifier + if !ok || s.Obj == nil { + return + } + call, ok := expr.High.(*ast.CallExpr) + // the high expression is a function call with a single argument + if !ok || len(call.Args) != 1 || call.Ellipsis.IsValid() { + return + } + fun, ok := call.Fun.(*ast.Ident) + // the function called is "len" and it is not locally defined; and + // because we don't have dot imports, it must be the predefined len() + if !ok || fun.Name != "len" || fun.Obj != nil { + return + } + arg, ok := call.Args[0].(*ast.Ident) + // the len argument is the array/slice object + if !ok || arg.Obj != s.Obj { + return + } + var b bytes.Buffer + printer.Fprint(&b, pass.Fset, expr.High) + pass.Report(analysis.Diagnostic{ + Pos: expr.High.Pos(), + End: expr.High.End(), + Message: fmt.Sprintf("unneeded: %s", b.String()), + SuggestedFixes: []analysis.SuggestedFix{{ + Message: fmt.Sprintf("Remove '%s'", b.String()), + TextEdits: []analysis.TextEdit{{ + Pos: expr.High.Pos(), + End: expr.High.End(), + NewText: []byte{}, + }}, + }}, + }) + }) + return nil, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/stubmethods/stubmethods.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/stubmethods/stubmethods.go new file mode 100644 index 0000000..09eb534 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/stubmethods/stubmethods.go @@ -0,0 +1,402 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package stubmethods + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/token" + "go/types" + "strconv" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typesinternal" +) + +const Doc = `stub methods analyzer + +This analyzer generates method stubs for concrete types +in order to implement a target interface` + +var Analyzer = &analysis.Analyzer{ + Name: "stubmethods", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, + RunDespiteErrors: true, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, err := range analysisinternal.GetTypeErrors(pass) { + ifaceErr := strings.Contains(err.Msg, "missing method") || strings.HasPrefix(err.Msg, "cannot convert") + if !ifaceErr { + continue + } + var file *ast.File + for _, f := range pass.Files { + if f.Pos() <= err.Pos && err.Pos < f.End() { + file = f + break + } + } + if file == nil { + continue + } + // Get the end position of the error. + _, _, endPos, ok := typesinternal.ReadGo116ErrorData(err) + if !ok { + var buf bytes.Buffer + if err := format.Node(&buf, pass.Fset, file); err != nil { + continue + } + endPos = analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos) + } + path, _ := astutil.PathEnclosingInterval(file, err.Pos, endPos) + si := GetStubInfo(pass.TypesInfo, path, err.Pos) + if si == nil { + continue + } + qf := RelativeToFiles(si.Concrete.Obj().Pkg(), file, nil, nil) + pass.Report(analysis.Diagnostic{ + Pos: err.Pos, + End: endPos, + Message: fmt.Sprintf("Implement %s", types.TypeString(si.Interface.Type(), qf)), + }) + } + return nil, nil +} + +// StubInfo represents a concrete type +// that wants to stub out an interface type +type StubInfo struct { + // Interface is the interface that the client wants to implement. + // When the interface is defined, the underlying object will be a TypeName. + // Note that we keep track of types.Object instead of types.Type in order + // to keep a reference to the declaring object's package and the ast file + // in the case where the concrete type file requires a new import that happens to be renamed + // in the interface file. + // TODO(marwan-at-work): implement interface literals. + Interface types.Object + Concrete *types.Named + Pointer bool +} + +// GetStubInfo determines whether the "missing method error" +// can be used to deduced what the concrete and interface types are. +func GetStubInfo(ti *types.Info, path []ast.Node, pos token.Pos) *StubInfo { + for _, n := range path { + switch n := n.(type) { + case *ast.ValueSpec: + return fromValueSpec(ti, n, pos) + case *ast.ReturnStmt: + // An error here may not indicate a real error the user should know about, but it may. + // Therefore, it would be best to log it out for debugging/reporting purposes instead of ignoring + // it. However, event.Log takes a context which is not passed via the analysis package. + // TODO(marwan-at-work): properly log this error. + si, _ := fromReturnStmt(ti, pos, path, n) + return si + case *ast.AssignStmt: + return fromAssignStmt(ti, n, pos) + case *ast.CallExpr: + // Note that some call expressions don't carry the interface type + // because they don't point to a function or method declaration elsewhere. + // For eaxmple, "var Interface = (*Concrete)(nil)". In that case, continue + // this loop to encounter other possibilities such as *ast.ValueSpec or others. + si := fromCallExpr(ti, pos, n) + if si != nil { + return si + } + } + } + return nil +} + +// fromCallExpr tries to find an *ast.CallExpr's function declaration and +// analyzes a function call's signature against the passed in parameter to deduce +// the concrete and interface types. +func fromCallExpr(ti *types.Info, pos token.Pos, ce *ast.CallExpr) *StubInfo { + paramIdx := -1 + for i, p := range ce.Args { + if pos >= p.Pos() && pos <= p.End() { + paramIdx = i + break + } + } + if paramIdx == -1 { + return nil + } + p := ce.Args[paramIdx] + concObj, pointer := concreteType(p, ti) + if concObj == nil || concObj.Obj().Pkg() == nil { + return nil + } + tv, ok := ti.Types[ce.Fun] + if !ok { + return nil + } + sig, ok := tv.Type.(*types.Signature) + if !ok { + return nil + } + sigVar := sig.Params().At(paramIdx) + iface := ifaceObjFromType(sigVar.Type()) + if iface == nil { + return nil + } + return &StubInfo{ + Concrete: concObj, + Pointer: pointer, + Interface: iface, + } +} + +// fromReturnStmt analyzes a "return" statement to extract +// a concrete type that is trying to be returned as an interface type. +// +// For example, func() io.Writer { return myType{} } +// would return StubInfo with the interface being io.Writer and the concrete type being myType{}. +func fromReturnStmt(ti *types.Info, pos token.Pos, path []ast.Node, rs *ast.ReturnStmt) (*StubInfo, error) { + returnIdx := -1 + for i, r := range rs.Results { + if pos >= r.Pos() && pos <= r.End() { + returnIdx = i + } + } + if returnIdx == -1 { + return nil, fmt.Errorf("pos %d not within return statement bounds: [%d-%d]", pos, rs.Pos(), rs.End()) + } + concObj, pointer := concreteType(rs.Results[returnIdx], ti) + if concObj == nil || concObj.Obj().Pkg() == nil { + return nil, nil + } + ef := enclosingFunction(path, ti) + if ef == nil { + return nil, fmt.Errorf("could not find the enclosing function of the return statement") + } + iface := ifaceType(ef.Results.List[returnIdx].Type, ti) + if iface == nil { + return nil, nil + } + return &StubInfo{ + Concrete: concObj, + Pointer: pointer, + Interface: iface, + }, nil +} + +// fromValueSpec returns *StubInfo from a variable declaration such as +// var x io.Writer = &T{} +func fromValueSpec(ti *types.Info, vs *ast.ValueSpec, pos token.Pos) *StubInfo { + var idx int + for i, vs := range vs.Values { + if pos >= vs.Pos() && pos <= vs.End() { + idx = i + break + } + } + + valueNode := vs.Values[idx] + ifaceNode := vs.Type + callExp, ok := valueNode.(*ast.CallExpr) + // if the ValueSpec is `var _ = myInterface(...)` + // as opposed to `var _ myInterface = ...` + if ifaceNode == nil && ok && len(callExp.Args) == 1 { + ifaceNode = callExp.Fun + valueNode = callExp.Args[0] + } + concObj, pointer := concreteType(valueNode, ti) + if concObj == nil || concObj.Obj().Pkg() == nil { + return nil + } + ifaceObj := ifaceType(ifaceNode, ti) + if ifaceObj == nil { + return nil + } + return &StubInfo{ + Concrete: concObj, + Interface: ifaceObj, + Pointer: pointer, + } +} + +// fromAssignStmt returns *StubInfo from a variable re-assignment such as +// var x io.Writer +// x = &T{} +func fromAssignStmt(ti *types.Info, as *ast.AssignStmt, pos token.Pos) *StubInfo { + idx := -1 + var lhs, rhs ast.Expr + // Given a re-assignment interface conversion error, + // the compiler error shows up on the right hand side of the expression. + // For example, x = &T{} where x is io.Writer highlights the error + // under "&T{}" and not "x". + for i, hs := range as.Rhs { + if pos >= hs.Pos() && pos <= hs.End() { + idx = i + break + } + } + if idx == -1 { + return nil + } + // Technically, this should never happen as + // we would get a "cannot assign N values to M variables" + // before we get an interface conversion error. Nonetheless, + // guard against out of range index errors. + if idx >= len(as.Lhs) { + return nil + } + lhs, rhs = as.Lhs[idx], as.Rhs[idx] + ifaceObj := ifaceType(lhs, ti) + if ifaceObj == nil { + return nil + } + concType, pointer := concreteType(rhs, ti) + if concType == nil || concType.Obj().Pkg() == nil { + return nil + } + return &StubInfo{ + Concrete: concType, + Interface: ifaceObj, + Pointer: pointer, + } +} + +// RelativeToFiles returns a types.Qualifier that formats package names +// according to the files where the concrete and interface types are defined. +// +// This is similar to types.RelativeTo except if a file imports the package with a different name, +// then it will use it. And if the file does import the package but it is ignored, +// then it will return the original name. It also prefers package names in ifaceFile in case +// an import is missing from concFile but is present in ifaceFile. +// +// Additionally, if missingImport is not nil, the function will be called whenever the concFile +// is presented with a package that is not imported. This is useful so that as types.TypeString is +// formatting a function signature, it is identifying packages that will need to be imported when +// stubbing an interface. +func RelativeToFiles(concPkg *types.Package, concFile, ifaceFile *ast.File, missingImport func(name, path string)) types.Qualifier { + return func(other *types.Package) string { + if other == concPkg { + return "" + } + + // Check if the concrete file already has the given import, + // if so return the default package name or the renamed import statement. + for _, imp := range concFile.Imports { + impPath, _ := strconv.Unquote(imp.Path.Value) + isIgnored := imp.Name != nil && (imp.Name.Name == "." || imp.Name.Name == "_") + if impPath == other.Path() && !isIgnored { + importName := other.Name() + if imp.Name != nil { + importName = imp.Name.Name + } + return importName + } + } + + // If the concrete file does not have the import, check if the package + // is renamed in the interface file and prefer that. + var importName string + if ifaceFile != nil { + for _, imp := range ifaceFile.Imports { + impPath, _ := strconv.Unquote(imp.Path.Value) + isIgnored := imp.Name != nil && (imp.Name.Name == "." || imp.Name.Name == "_") + if impPath == other.Path() && !isIgnored { + if imp.Name != nil && imp.Name.Name != concPkg.Name() { + importName = imp.Name.Name + } + break + } + } + } + + if missingImport != nil { + missingImport(importName, other.Path()) + } + + // Up until this point, importName must stay empty when calling missingImport, + // otherwise we'd end up with `import time "time"` which doesn't look idiomatic. + if importName == "" { + importName = other.Name() + } + return importName + } +} + +// ifaceType will try to extract the types.Object that defines +// the interface given the ast.Expr where the "missing method" +// or "conversion" errors happen. +func ifaceType(n ast.Expr, ti *types.Info) types.Object { + tv, ok := ti.Types[n] + if !ok { + return nil + } + return ifaceObjFromType(tv.Type) +} + +func ifaceObjFromType(t types.Type) types.Object { + named, ok := t.(*types.Named) + if !ok { + return nil + } + _, ok = named.Underlying().(*types.Interface) + if !ok { + return nil + } + // Interfaces defined in the "builtin" package return nil a Pkg(). + // But they are still real interfaces that we need to make a special case for. + // Therefore, protect gopls from panicking if a new interface type was added in the future. + if named.Obj().Pkg() == nil && named.Obj().Name() != "error" { + return nil + } + return named.Obj() +} + +// concreteType tries to extract the *types.Named that defines +// the concrete type given the ast.Expr where the "missing method" +// or "conversion" errors happened. If the concrete type is something +// that cannot have methods defined on it (such as basic types), this +// method will return a nil *types.Named. The second return parameter +// is a boolean that indicates whether the concreteType was defined as a +// pointer or value. +func concreteType(n ast.Expr, ti *types.Info) (*types.Named, bool) { + tv, ok := ti.Types[n] + if !ok { + return nil, false + } + typ := tv.Type + ptr, isPtr := typ.(*types.Pointer) + if isPtr { + typ = ptr.Elem() + } + named, ok := typ.(*types.Named) + if !ok { + return nil, false + } + return named, isPtr +} + +// enclosingFunction returns the signature and type of the function +// enclosing the given position. +func enclosingFunction(path []ast.Node, info *types.Info) *ast.FuncType { + for _, node := range path { + switch t := node.(type) { + case *ast.FuncDecl: + if _, ok := info.Defs[t.Name]; ok { + return t.Type + } + case *ast.FuncLit: + if _, ok := info.Types[t]; ok { + return t.Type + } + } + } + return nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/undeclaredname/undeclared.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/undeclaredname/undeclared.go new file mode 100644 index 0000000..1f6989e --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/undeclaredname/undeclared.go @@ -0,0 +1,347 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package undeclaredname defines an Analyzer that applies suggested fixes +// to errors of the type "undeclared name: %s". +package undeclaredname + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/token" + "go/types" + "strings" + "unicode" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" +) + +const Doc = `suggested fixes for "undeclared name: <>" + +This checker provides suggested fixes for type errors of the +type "undeclared name: <>". It will either insert a new statement, +such as: + +"<> := " + +or a new function declaration, such as: + +func <>(inferred parameters) { + panic("implement me!") +} +` + +var Analyzer = &analysis.Analyzer{ + Name: string(analysisinternal.UndeclaredName), + Doc: Doc, + Requires: []*analysis.Analyzer{}, + Run: run, + RunDespiteErrors: true, +} + +// The prefix for this error message changed in Go 1.20. +var undeclaredNamePrefixes = []string{"undeclared name: ", "undefined: "} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, err := range analysisinternal.GetTypeErrors(pass) { + runForError(pass, err) + } + return nil, nil +} + +func runForError(pass *analysis.Pass, err types.Error) { + var name string + for _, prefix := range undeclaredNamePrefixes { + if !strings.HasPrefix(err.Msg, prefix) { + continue + } + name = strings.TrimPrefix(err.Msg, prefix) + } + if name == "" { + return + } + var file *ast.File + for _, f := range pass.Files { + if f.Pos() <= err.Pos && err.Pos < f.End() { + file = f + break + } + } + if file == nil { + return + } + + // Get the path for the relevant range. + path, _ := astutil.PathEnclosingInterval(file, err.Pos, err.Pos) + if len(path) < 2 { + return + } + ident, ok := path[0].(*ast.Ident) + if !ok || ident.Name != name { + return + } + + // Undeclared quick fixes only work in function bodies. + inFunc := false + for i := range path { + if _, inFunc = path[i].(*ast.FuncDecl); inFunc { + if i == 0 { + return + } + if _, isBody := path[i-1].(*ast.BlockStmt); !isBody { + return + } + break + } + } + if !inFunc { + return + } + // Skip selector expressions because it might be too complex + // to try and provide a suggested fix for fields and methods. + if _, ok := path[1].(*ast.SelectorExpr); ok { + return + } + tok := pass.Fset.File(file.Pos()) + if tok == nil { + return + } + offset := pass.Fset.Position(err.Pos).Offset + end := tok.Pos(offset + len(name)) + pass.Report(analysis.Diagnostic{ + Pos: err.Pos, + End: end, + Message: err.Msg, + }) +} + +func SuggestedFix(fset *token.FileSet, rng span.Range, content []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) { + pos := rng.Start // don't use the end + path, _ := astutil.PathEnclosingInterval(file, pos, pos) + if len(path) < 2 { + return nil, fmt.Errorf("no expression found") + } + ident, ok := path[0].(*ast.Ident) + if !ok { + return nil, fmt.Errorf("no identifier found") + } + + // Check for a possible call expression, in which case we should add a + // new function declaration. + if len(path) > 1 { + if _, ok := path[1].(*ast.CallExpr); ok { + return newFunctionDeclaration(path, file, pkg, info, fset) + } + } + + // Get the place to insert the new statement. + insertBeforeStmt := analysisinternal.StmtToInsertVarBefore(path) + if insertBeforeStmt == nil { + return nil, fmt.Errorf("could not locate insertion point") + } + + insertBefore := fset.Position(insertBeforeStmt.Pos()).Offset + + // Get the indent to add on the line after the new statement. + // Since this will have a parse error, we can not use format.Source(). + contentBeforeStmt, indent := content[:insertBefore], "\n" + if nl := bytes.LastIndex(contentBeforeStmt, []byte("\n")); nl != -1 { + indent = string(contentBeforeStmt[nl:]) + } + + // Create the new local variable statement. + newStmt := fmt.Sprintf("%s := %s", ident.Name, indent) + return &analysis.SuggestedFix{ + Message: fmt.Sprintf("Create variable \"%s\"", ident.Name), + TextEdits: []analysis.TextEdit{{ + Pos: insertBeforeStmt.Pos(), + End: insertBeforeStmt.Pos(), + NewText: []byte(newStmt), + }}, + }, nil +} + +func newFunctionDeclaration(path []ast.Node, file *ast.File, pkg *types.Package, info *types.Info, fset *token.FileSet) (*analysis.SuggestedFix, error) { + if len(path) < 3 { + return nil, fmt.Errorf("unexpected set of enclosing nodes: %v", path) + } + ident, ok := path[0].(*ast.Ident) + if !ok { + return nil, fmt.Errorf("no name for function declaration %v (%T)", path[0], path[0]) + } + call, ok := path[1].(*ast.CallExpr) + if !ok { + return nil, fmt.Errorf("no call expression found %v (%T)", path[1], path[1]) + } + + // Find the enclosing function, so that we can add the new declaration + // below. + var enclosing *ast.FuncDecl + for _, n := range path { + if n, ok := n.(*ast.FuncDecl); ok { + enclosing = n + break + } + } + // TODO(rstambler): Support the situation when there is no enclosing + // function. + if enclosing == nil { + return nil, fmt.Errorf("no enclosing function found: %v", path) + } + + pos := enclosing.End() + + var paramNames []string + var paramTypes []types.Type + // keep track of all param names to later ensure uniqueness + nameCounts := map[string]int{} + for _, arg := range call.Args { + typ := info.TypeOf(arg) + if typ == nil { + return nil, fmt.Errorf("unable to determine type for %s", arg) + } + + switch t := typ.(type) { + // this is the case where another function call returning multiple + // results is used as an argument + case *types.Tuple: + n := t.Len() + for i := 0; i < n; i++ { + name := typeToArgName(t.At(i).Type()) + nameCounts[name]++ + + paramNames = append(paramNames, name) + paramTypes = append(paramTypes, types.Default(t.At(i).Type())) + } + + default: + // does the argument have a name we can reuse? + // only happens in case of a *ast.Ident + var name string + if ident, ok := arg.(*ast.Ident); ok { + name = ident.Name + } + + if name == "" { + name = typeToArgName(typ) + } + + nameCounts[name]++ + + paramNames = append(paramNames, name) + paramTypes = append(paramTypes, types.Default(typ)) + } + } + + for n, c := range nameCounts { + // Any names we saw more than once will need a unique suffix added + // on. Reset the count to 1 to act as the suffix for the first + // occurrence of that name. + if c >= 2 { + nameCounts[n] = 1 + } else { + delete(nameCounts, n) + } + } + + params := &ast.FieldList{} + + for i, name := range paramNames { + if suffix, repeats := nameCounts[name]; repeats { + nameCounts[name]++ + name = fmt.Sprintf("%s%d", name, suffix) + } + + // only worth checking after previous param in the list + if i > 0 { + // if type of parameter at hand is the same as the previous one, + // add it to the previous param list of identifiers so to have: + // (s1, s2 string) + // and not + // (s1 string, s2 string) + if paramTypes[i] == paramTypes[i-1] { + params.List[len(params.List)-1].Names = append(params.List[len(params.List)-1].Names, ast.NewIdent(name)) + continue + } + } + + params.List = append(params.List, &ast.Field{ + Names: []*ast.Ident{ + ast.NewIdent(name), + }, + Type: analysisinternal.TypeExpr(file, pkg, paramTypes[i]), + }) + } + + decl := &ast.FuncDecl{ + Name: ast.NewIdent(ident.Name), + Type: &ast.FuncType{ + Params: params, + // TODO(rstambler): Also handle result parameters here. + }, + Body: &ast.BlockStmt{ + List: []ast.Stmt{ + &ast.ExprStmt{ + X: &ast.CallExpr{ + Fun: ast.NewIdent("panic"), + Args: []ast.Expr{ + &ast.BasicLit{ + Value: `"unimplemented"`, + }, + }, + }, + }, + }, + }, + } + + b := bytes.NewBufferString("\n\n") + if err := format.Node(b, fset, decl); err != nil { + return nil, err + } + return &analysis.SuggestedFix{ + Message: fmt.Sprintf("Create function \"%s\"", ident.Name), + TextEdits: []analysis.TextEdit{{ + Pos: pos, + End: pos, + NewText: b.Bytes(), + }}, + }, nil +} +func typeToArgName(ty types.Type) string { + s := types.Default(ty).String() + + switch t := ty.(type) { + case *types.Basic: + // use first letter in type name for basic types + return s[0:1] + case *types.Slice: + // use element type to decide var name for slices + return typeToArgName(t.Elem()) + case *types.Array: + // use element type to decide var name for arrays + return typeToArgName(t.Elem()) + case *types.Chan: + return "ch" + } + + s = strings.TrimFunc(s, func(r rune) bool { + return !unicode.IsLetter(r) + }) + + if s == "error" { + return "err" + } + + // remove package (if present) + // and make first letter lowercase + a := []rune(s[strings.LastIndexByte(s, '.')+1:]) + a[0] = unicode.ToLower(a[0]) + return string(a) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedparams/unusedparams.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedparams/unusedparams.go new file mode 100644 index 0000000..4c933c8 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedparams/unusedparams.go @@ -0,0 +1,152 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package unusedparams defines an analyzer that checks for unused +// parameters of functions. +package unusedparams + +import ( + "fmt" + "go/ast" + "go/types" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const Doc = `check for unused parameters of functions + +The unusedparams analyzer checks functions to see if there are +any parameters that are not being used. + +To reduce false positives it ignores: +- methods +- parameters that do not have a name or are underscored +- functions in test files +- functions with empty bodies or those with just a return stmt` + +var Analyzer = &analysis.Analyzer{ + Name: "unusedparams", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +type paramData struct { + field *ast.Field + ident *ast.Ident + typObj types.Object +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.FuncDecl)(nil), + (*ast.FuncLit)(nil), + } + + inspect.Preorder(nodeFilter, func(n ast.Node) { + var fieldList *ast.FieldList + var body *ast.BlockStmt + + // Get the fieldList and body from the function node. + switch f := n.(type) { + case *ast.FuncDecl: + fieldList, body = f.Type.Params, f.Body + // TODO(golang/go#36602): add better handling for methods, if we enable methods + // we will get false positives if a struct is potentially implementing + // an interface. + if f.Recv != nil { + return + } + // Ignore functions in _test.go files to reduce false positives. + if file := pass.Fset.File(n.Pos()); file != nil && strings.HasSuffix(file.Name(), "_test.go") { + return + } + case *ast.FuncLit: + fieldList, body = f.Type.Params, f.Body + } + // If there are no arguments or the function is empty, then return. + if fieldList.NumFields() == 0 || body == nil || len(body.List) == 0 { + return + } + + switch expr := body.List[0].(type) { + case *ast.ReturnStmt: + // Ignore functions that only contain a return statement to reduce false positives. + return + case *ast.ExprStmt: + callExpr, ok := expr.X.(*ast.CallExpr) + if !ok || len(body.List) > 1 { + break + } + // Ignore functions that only contain a panic statement to reduce false positives. + if fun, ok := callExpr.Fun.(*ast.Ident); ok && fun.Name == "panic" { + return + } + } + + // Get the useful data from each field. + params := make(map[string]*paramData) + unused := make(map[*paramData]bool) + for _, f := range fieldList.List { + for _, i := range f.Names { + if i.Name == "_" { + continue + } + params[i.Name] = ¶mData{ + field: f, + ident: i, + typObj: pass.TypesInfo.ObjectOf(i), + } + unused[params[i.Name]] = true + } + } + + // Traverse through the body of the function and + // check to see which parameters are unused. + ast.Inspect(body, func(node ast.Node) bool { + n, ok := node.(*ast.Ident) + if !ok { + return true + } + param, ok := params[n.Name] + if !ok { + return false + } + if nObj := pass.TypesInfo.ObjectOf(n); nObj != param.typObj { + return false + } + delete(unused, param) + return false + }) + + // Create the reports for the unused parameters. + for u := range unused { + start, end := u.field.Pos(), u.field.End() + if len(u.field.Names) > 1 { + start, end = u.ident.Pos(), u.ident.End() + } + // TODO(golang/go#36602): Add suggested fixes to automatically + // remove the unused parameter from every use of this + // function. + pass.Report(analysis.Diagnostic{ + Pos: start, + End: end, + Message: fmt.Sprintf("potentially unused parameter: '%s'", u.ident.Name), + SuggestedFixes: []analysis.SuggestedFix{{ + Message: `Replace with "_"`, + TextEdits: []analysis.TextEdit{{ + Pos: u.ident.Pos(), + End: u.ident.End(), + NewText: []byte("_"), + }}, + }}, + }) + } + }) + return nil, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedvariable/unusedvariable.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedvariable/unusedvariable.go new file mode 100644 index 0000000..edf5692 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedvariable/unusedvariable.go @@ -0,0 +1,301 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package unusedvariable defines an analyzer that checks for unused variables. +package unusedvariable + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/token" + "go/types" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" +) + +const Doc = `check for unused variables + +The unusedvariable analyzer suggests fixes for unused variables errors. +` + +var Analyzer = &analysis.Analyzer{ + Name: "unusedvariable", + Doc: Doc, + Requires: []*analysis.Analyzer{}, + Run: run, + RunDespiteErrors: true, // an unusedvariable diagnostic is a compile error +} + +// The suffix for this error message changed in Go 1.20. +var unusedVariableSuffixes = []string{" declared and not used", " declared but not used"} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, typeErr := range analysisinternal.GetTypeErrors(pass) { + for _, suffix := range unusedVariableSuffixes { + if strings.HasSuffix(typeErr.Msg, suffix) { + varName := strings.TrimSuffix(typeErr.Msg, suffix) + err := runForError(pass, typeErr, varName) + if err != nil { + return nil, err + } + } + } + } + + return nil, nil +} + +func runForError(pass *analysis.Pass, err types.Error, name string) error { + var file *ast.File + for _, f := range pass.Files { + if f.Pos() <= err.Pos && err.Pos < f.End() { + file = f + break + } + } + if file == nil { + return nil + } + + path, _ := astutil.PathEnclosingInterval(file, err.Pos, err.Pos) + if len(path) < 2 { + return nil + } + + ident, ok := path[0].(*ast.Ident) + if !ok || ident.Name != name { + return nil + } + + diag := analysis.Diagnostic{ + Pos: ident.Pos(), + End: ident.End(), + Message: err.Msg, + } + + for i := range path { + switch stmt := path[i].(type) { + case *ast.ValueSpec: + // Find GenDecl to which offending ValueSpec belongs. + if decl, ok := path[i+1].(*ast.GenDecl); ok { + fixes := removeVariableFromSpec(pass, path, stmt, decl, ident) + // fixes may be nil + if len(fixes) > 0 { + diag.SuggestedFixes = fixes + pass.Report(diag) + } + } + + case *ast.AssignStmt: + if stmt.Tok != token.DEFINE { + continue + } + + containsIdent := false + for _, expr := range stmt.Lhs { + if expr == ident { + containsIdent = true + } + } + if !containsIdent { + continue + } + + fixes := removeVariableFromAssignment(pass, path, stmt, ident) + // fixes may be nil + if len(fixes) > 0 { + diag.SuggestedFixes = fixes + pass.Report(diag) + } + } + } + + return nil +} + +func removeVariableFromSpec(pass *analysis.Pass, path []ast.Node, stmt *ast.ValueSpec, decl *ast.GenDecl, ident *ast.Ident) []analysis.SuggestedFix { + newDecl := new(ast.GenDecl) + *newDecl = *decl + newDecl.Specs = nil + + for _, spec := range decl.Specs { + if spec != stmt { + newDecl.Specs = append(newDecl.Specs, spec) + continue + } + + newSpec := new(ast.ValueSpec) + *newSpec = *stmt + newSpec.Names = nil + + for _, n := range stmt.Names { + if n != ident { + newSpec.Names = append(newSpec.Names, n) + } + } + + if len(newSpec.Names) > 0 { + newDecl.Specs = append(newDecl.Specs, newSpec) + } + } + + // decl.End() does not include any comments, so if a comment is present we + // need to account for it when we delete the statement + end := decl.End() + if stmt.Comment != nil && stmt.Comment.End() > end { + end = stmt.Comment.End() + } + + // There are no other specs left in the declaration, the whole statement can + // be deleted + if len(newDecl.Specs) == 0 { + // Find parent DeclStmt and delete it + for _, node := range path { + if declStmt, ok := node.(*ast.DeclStmt); ok { + return []analysis.SuggestedFix{ + { + Message: suggestedFixMessage(ident.Name), + TextEdits: deleteStmtFromBlock(path, declStmt), + }, + } + } + } + } + + var b bytes.Buffer + if err := format.Node(&b, pass.Fset, newDecl); err != nil { + return nil + } + + return []analysis.SuggestedFix{ + { + Message: suggestedFixMessage(ident.Name), + TextEdits: []analysis.TextEdit{ + { + Pos: decl.Pos(), + // Avoid adding a new empty line + End: end + 1, + NewText: b.Bytes(), + }, + }, + }, + } +} + +func removeVariableFromAssignment(pass *analysis.Pass, path []ast.Node, stmt *ast.AssignStmt, ident *ast.Ident) []analysis.SuggestedFix { + // The only variable in the assignment is unused + if len(stmt.Lhs) == 1 { + // If LHS has only one expression to be valid it has to have 1 expression + // on RHS + // + // RHS may have side effects, preserve RHS + if exprMayHaveSideEffects(stmt.Rhs[0]) { + // Delete until RHS + return []analysis.SuggestedFix{ + { + Message: suggestedFixMessage(ident.Name), + TextEdits: []analysis.TextEdit{ + { + Pos: ident.Pos(), + End: stmt.Rhs[0].Pos(), + }, + }, + }, + } + } + + // RHS does not have any side effects, delete the whole statement + return []analysis.SuggestedFix{ + { + Message: suggestedFixMessage(ident.Name), + TextEdits: deleteStmtFromBlock(path, stmt), + }, + } + } + + // Otherwise replace ident with `_` + return []analysis.SuggestedFix{ + { + Message: suggestedFixMessage(ident.Name), + TextEdits: []analysis.TextEdit{ + { + Pos: ident.Pos(), + End: ident.End(), + NewText: []byte("_"), + }, + }, + }, + } +} + +func suggestedFixMessage(name string) string { + return fmt.Sprintf("Remove variable %s", name) +} + +func deleteStmtFromBlock(path []ast.Node, stmt ast.Stmt) []analysis.TextEdit { + // Find innermost enclosing BlockStmt. + var block *ast.BlockStmt + for i := range path { + if blockStmt, ok := path[i].(*ast.BlockStmt); ok { + block = blockStmt + break + } + } + + nodeIndex := -1 + for i, blockStmt := range block.List { + if blockStmt == stmt { + nodeIndex = i + break + } + } + + // The statement we need to delete was not found in BlockStmt + if nodeIndex == -1 { + return nil + } + + // Delete until the end of the block unless there is another statement after + // the one we are trying to delete + end := block.Rbrace + if nodeIndex < len(block.List)-1 { + end = block.List[nodeIndex+1].Pos() + } + + return []analysis.TextEdit{ + { + Pos: stmt.Pos(), + End: end, + }, + } +} + +// exprMayHaveSideEffects reports whether the expression may have side effects +// (because it contains a function call or channel receive). We disregard +// runtime panics as well written programs should not encounter them. +func exprMayHaveSideEffects(expr ast.Expr) bool { + var mayHaveSideEffects bool + ast.Inspect(expr, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.CallExpr: // possible function call + mayHaveSideEffects = true + return false + case *ast.UnaryExpr: + if n.Op == token.ARROW { // channel receive + mayHaveSideEffects = true + return false + } + case *ast.FuncLit: + return false // evaluating what's inside a FuncLit has no effect + } + return true + }) + + return mayHaveSideEffects +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/analysis/useany/useany.go b/gopls/golang_org_x_tools_gopls/lsp/analysis/useany/useany.go new file mode 100644 index 0000000..14b81cd --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/analysis/useany/useany.go @@ -0,0 +1,102 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package useany defines an Analyzer that checks for usage of interface{} in +// constraints, rather than the predeclared any. +package useany + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +const Doc = `check for constraints that could be simplified to "any"` + +var Analyzer = &analysis.Analyzer{ + Name: "useany", + Doc: Doc, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + universeAny := types.Universe.Lookup("any") + if universeAny == nil { + // Go <= 1.17. Nothing to check. + return nil, nil + } + + nodeFilter := []ast.Node{ + (*ast.TypeSpec)(nil), + (*ast.FuncType)(nil), + } + + inspect.Preorder(nodeFilter, func(node ast.Node) { + var tparams *ast.FieldList + switch node := node.(type) { + case *ast.TypeSpec: + tparams = typeparams.ForTypeSpec(node) + case *ast.FuncType: + tparams = typeparams.ForFuncType(node) + default: + panic(fmt.Sprintf("unexpected node type %T", node)) + } + if tparams.NumFields() == 0 { + return + } + + for _, field := range tparams.List { + typ := pass.TypesInfo.Types[field.Type].Type + if typ == nil { + continue // something is wrong, but not our concern + } + iface, ok := typ.Underlying().(*types.Interface) + if !ok { + continue // invalid constraint + } + + // If the constraint is the empty interface, offer a fix to use 'any' + // instead. + if iface.Empty() { + id, _ := field.Type.(*ast.Ident) + if id != nil && pass.TypesInfo.Uses[id] == universeAny { + continue + } + + diag := analysis.Diagnostic{ + Pos: field.Type.Pos(), + End: field.Type.End(), + Message: `could use "any" for this empty interface`, + } + + // Only suggest a fix to 'any' if we actually resolve the predeclared + // any in this scope. + if scope := pass.TypesInfo.Scopes[node]; scope != nil { + if _, any := scope.LookupParent("any", token.NoPos); any == universeAny { + diag.SuggestedFixes = []analysis.SuggestedFix{{ + Message: `use "any"`, + TextEdits: []analysis.TextEdit{{ + Pos: field.Type.Pos(), + End: field.Type.End(), + NewText: []byte("any"), + }}, + }} + } + } + + pass.Report(diag) + } + } + }) + return nil, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/command/command_gen.go b/gopls/golang_org_x_tools_gopls/lsp/command/command_gen.go new file mode 100644 index 0000000..cdbeb17 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/command/command_gen.go @@ -0,0 +1,493 @@ +// 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. + +// Don't include this file during code generation, or it will break the build +// if existing interface methods have been modified. +//go:build !generate +// +build !generate + +package command + +// Code generated by generate.go. DO NOT EDIT. + +import ( + "context" + "fmt" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +const ( + AddDependency Command = "add_dependency" + AddImport Command = "add_import" + ApplyFix Command = "apply_fix" + CheckUpgrades Command = "check_upgrades" + EditGoDirective Command = "edit_go_directive" + GCDetails Command = "gc_details" + Generate Command = "generate" + GenerateGoplsMod Command = "generate_gopls_mod" + GoGetPackage Command = "go_get_package" + ListImports Command = "list_imports" + ListKnownPackages Command = "list_known_packages" + RegenerateCgo Command = "regenerate_cgo" + RemoveDependency Command = "remove_dependency" + ResetGoModDiagnostics Command = "reset_go_mod_diagnostics" + RunTests Command = "run_tests" + RunVulncheckExp Command = "run_vulncheck_exp" + StartDebugging Command = "start_debugging" + Test Command = "test" + Tidy Command = "tidy" + ToggleGCDetails Command = "toggle_gc_details" + UpdateGoSum Command = "update_go_sum" + UpgradeDependency Command = "upgrade_dependency" + Vendor Command = "vendor" +) + +var Commands = []Command{ + AddDependency, + AddImport, + ApplyFix, + CheckUpgrades, + EditGoDirective, + GCDetails, + Generate, + GenerateGoplsMod, + GoGetPackage, + ListImports, + ListKnownPackages, + RegenerateCgo, + RemoveDependency, + ResetGoModDiagnostics, + RunTests, + RunVulncheckExp, + StartDebugging, + Test, + Tidy, + ToggleGCDetails, + UpdateGoSum, + UpgradeDependency, + Vendor, +} + +func Dispatch(ctx context.Context, params *protocol.ExecuteCommandParams, s Interface) (interface{}, error) { + switch params.Command { + case "gopls.add_dependency": + var a0 DependencyArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.AddDependency(ctx, a0) + case "gopls.add_import": + var a0 AddImportArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.AddImport(ctx, a0) + case "gopls.apply_fix": + var a0 ApplyFixArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.ApplyFix(ctx, a0) + case "gopls.check_upgrades": + var a0 CheckUpgradesArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.CheckUpgrades(ctx, a0) + case "gopls.edit_go_directive": + var a0 EditGoDirectiveArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.EditGoDirective(ctx, a0) + case "gopls.gc_details": + var a0 protocol.DocumentURI + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.GCDetails(ctx, a0) + case "gopls.generate": + var a0 GenerateArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.Generate(ctx, a0) + case "gopls.generate_gopls_mod": + var a0 URIArg + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.GenerateGoplsMod(ctx, a0) + case "gopls.go_get_package": + var a0 GoGetPackageArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.GoGetPackage(ctx, a0) + case "gopls.list_imports": + var a0 URIArg + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return s.ListImports(ctx, a0) + case "gopls.list_known_packages": + var a0 URIArg + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return s.ListKnownPackages(ctx, a0) + case "gopls.regenerate_cgo": + var a0 URIArg + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.RegenerateCgo(ctx, a0) + case "gopls.remove_dependency": + var a0 RemoveDependencyArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.RemoveDependency(ctx, a0) + case "gopls.reset_go_mod_diagnostics": + var a0 URIArg + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.ResetGoModDiagnostics(ctx, a0) + case "gopls.run_tests": + var a0 RunTestsArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.RunTests(ctx, a0) + case "gopls.run_vulncheck_exp": + var a0 VulncheckArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.RunVulncheckExp(ctx, a0) + case "gopls.start_debugging": + var a0 DebuggingArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return s.StartDebugging(ctx, a0) + case "gopls.test": + var a0 protocol.DocumentURI + var a1 []string + var a2 []string + if err := UnmarshalArgs(params.Arguments, &a0, &a1, &a2); err != nil { + return nil, err + } + return nil, s.Test(ctx, a0, a1, a2) + case "gopls.tidy": + var a0 URIArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.Tidy(ctx, a0) + case "gopls.toggle_gc_details": + var a0 URIArg + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.ToggleGCDetails(ctx, a0) + case "gopls.update_go_sum": + var a0 URIArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.UpdateGoSum(ctx, a0) + case "gopls.upgrade_dependency": + var a0 DependencyArgs + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.UpgradeDependency(ctx, a0) + case "gopls.vendor": + var a0 URIArg + if err := UnmarshalArgs(params.Arguments, &a0); err != nil { + return nil, err + } + return nil, s.Vendor(ctx, a0) + } + return nil, fmt.Errorf("unsupported command %q", params.Command) +} + +func NewAddDependencyCommand(title string, a0 DependencyArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.add_dependency", + Arguments: args, + }, nil +} + +func NewAddImportCommand(title string, a0 AddImportArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.add_import", + Arguments: args, + }, nil +} + +func NewApplyFixCommand(title string, a0 ApplyFixArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.apply_fix", + Arguments: args, + }, nil +} + +func NewCheckUpgradesCommand(title string, a0 CheckUpgradesArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.check_upgrades", + Arguments: args, + }, nil +} + +func NewEditGoDirectiveCommand(title string, a0 EditGoDirectiveArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.edit_go_directive", + Arguments: args, + }, nil +} + +func NewGCDetailsCommand(title string, a0 protocol.DocumentURI) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.gc_details", + Arguments: args, + }, nil +} + +func NewGenerateCommand(title string, a0 GenerateArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.generate", + Arguments: args, + }, nil +} + +func NewGenerateGoplsModCommand(title string, a0 URIArg) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.generate_gopls_mod", + Arguments: args, + }, nil +} + +func NewGoGetPackageCommand(title string, a0 GoGetPackageArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.go_get_package", + Arguments: args, + }, nil +} + +func NewListImportsCommand(title string, a0 URIArg) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.list_imports", + Arguments: args, + }, nil +} + +func NewListKnownPackagesCommand(title string, a0 URIArg) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.list_known_packages", + Arguments: args, + }, nil +} + +func NewRegenerateCgoCommand(title string, a0 URIArg) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.regenerate_cgo", + Arguments: args, + }, nil +} + +func NewRemoveDependencyCommand(title string, a0 RemoveDependencyArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.remove_dependency", + Arguments: args, + }, nil +} + +func NewResetGoModDiagnosticsCommand(title string, a0 URIArg) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.reset_go_mod_diagnostics", + Arguments: args, + }, nil +} + +func NewRunTestsCommand(title string, a0 RunTestsArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.run_tests", + Arguments: args, + }, nil +} + +func NewRunVulncheckExpCommand(title string, a0 VulncheckArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.run_vulncheck_exp", + Arguments: args, + }, nil +} + +func NewStartDebuggingCommand(title string, a0 DebuggingArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.start_debugging", + Arguments: args, + }, nil +} + +func NewTestCommand(title string, a0 protocol.DocumentURI, a1 []string, a2 []string) (protocol.Command, error) { + args, err := MarshalArgs(a0, a1, a2) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.test", + Arguments: args, + }, nil +} + +func NewTidyCommand(title string, a0 URIArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.tidy", + Arguments: args, + }, nil +} + +func NewToggleGCDetailsCommand(title string, a0 URIArg) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.toggle_gc_details", + Arguments: args, + }, nil +} + +func NewUpdateGoSumCommand(title string, a0 URIArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.update_go_sum", + Arguments: args, + }, nil +} + +func NewUpgradeDependencyCommand(title string, a0 DependencyArgs) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.upgrade_dependency", + Arguments: args, + }, nil +} + +func NewVendorCommand(title string, a0 URIArg) (protocol.Command, error) { + args, err := MarshalArgs(a0) + if err != nil { + return protocol.Command{}, err + } + return protocol.Command{ + Title: title, + Command: "gopls.vendor", + Arguments: args, + }, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/command/interface.go b/gopls/golang_org_x_tools_gopls/lsp/command/interface.go new file mode 100644 index 0000000..6f71e2d --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/command/interface.go @@ -0,0 +1,379 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package command defines the interface provided by gopls for the +// workspace/executeCommand LSP request. +// +// This interface is fully specified by the Interface type, provided it +// conforms to the restrictions outlined in its doc string. +// +// Bindings for server-side command dispatch and client-side serialization are +// also provided by this package, via code generation. +package command + +//go:generate go run -tags=generate generate.go + +import ( + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +// Interface defines the interface gopls exposes for the +// workspace/executeCommand request. +// +// This interface is used to generate marshaling/unmarshaling code, dispatch, +// and documentation, and so has some additional restrictions: +// 1. All method arguments must be JSON serializable. +// 2. Methods must return either error or (T, error), where T is a +// JSON serializable type. +// 3. The first line of the doc string is special. Everything after the colon +// is considered the command 'Title'. +// TODO(rFindley): reconsider this -- Title may be unnecessary. +type Interface interface { + // ApplyFix: Apply a fix + // + // Applies a fix to a region of source code. + ApplyFix(context.Context, ApplyFixArgs) error + // Test: Run test(s) (legacy) + // + // Runs `go test` for a specific set of test or benchmark functions. + Test(context.Context, protocol.DocumentURI, []string, []string) error + + // TODO: deprecate Test in favor of RunTests below. + + // Test: Run test(s) + // + // Runs `go test` for a specific set of test or benchmark functions. + RunTests(context.Context, RunTestsArgs) error + + // Generate: Run go generate + // + // Runs `go generate` for a given directory. + Generate(context.Context, GenerateArgs) error + + // RegenerateCgo: Regenerate cgo + // + // Regenerates cgo definitions. + RegenerateCgo(context.Context, URIArg) error + + // Tidy: Run go mod tidy + // + // Runs `go mod tidy` for a module. + Tidy(context.Context, URIArgs) error + + // Vendor: Run go mod vendor + // + // Runs `go mod vendor` for a module. + Vendor(context.Context, URIArg) error + + // EditGoDirective: Run go mod edit -go=version + // + // Runs `go mod edit -go=version` for a module. + EditGoDirective(context.Context, EditGoDirectiveArgs) error + + // UpdateGoSum: Update go.sum + // + // Updates the go.sum file for a module. + UpdateGoSum(context.Context, URIArgs) error + + // CheckUpgrades: Check for upgrades + // + // Checks for module upgrades. + CheckUpgrades(context.Context, CheckUpgradesArgs) error + + // AddDependency: Add a dependency + // + // Adds a dependency to the go.mod file for a module. + AddDependency(context.Context, DependencyArgs) error + + // UpgradeDependency: Upgrade a dependency + // + // Upgrades a dependency in the go.mod file for a module. + UpgradeDependency(context.Context, DependencyArgs) error + + // RemoveDependency: Remove a dependency + // + // Removes a dependency from the go.mod file of a module. + RemoveDependency(context.Context, RemoveDependencyArgs) error + + // ResetGoModDiagnostics: Reset go.mod diagnostics + // + // Reset diagnostics in the go.mod file of a module. + ResetGoModDiagnostics(context.Context, URIArg) error + + // GoGetPackage: go get a package + // + // Runs `go get` to fetch a package. + GoGetPackage(context.Context, GoGetPackageArgs) error + + // GCDetails: Toggle gc_details + // + // Toggle the calculation of gc annotations. + GCDetails(context.Context, protocol.DocumentURI) error + + // TODO: deprecate GCDetails in favor of ToggleGCDetails below. + + // ToggleGCDetails: Toggle gc_details + // + // Toggle the calculation of gc annotations. + ToggleGCDetails(context.Context, URIArg) error + + // GenerateGoplsMod: Generate gopls.mod + // + // (Re)generate the gopls.mod file for a workspace. + GenerateGoplsMod(context.Context, URIArg) error + + // ListKnownPackages: List known packages + // + // Retrieve a list of packages that are importable from the given URI. + ListKnownPackages(context.Context, URIArg) (ListKnownPackagesResult, error) + + // ListImports: List imports of a file and its package + // + // Retrieve a list of imports in the given Go file, and the package it + // belongs to. + ListImports(context.Context, URIArg) (ListImportsResult, error) + + // AddImport: Add an import + // + // Ask the server to add an import path to a given Go file. The method will + // call applyEdit on the client so that clients don't have to apply the edit + // themselves. + AddImport(context.Context, AddImportArgs) error + + // StartDebugging: Start the gopls debug server + // + // Start the gopls debug server if it isn't running, and return the debug + // address. + StartDebugging(context.Context, DebuggingArgs) (DebuggingResult, error) + + // RunVulncheckExp: Run vulncheck (experimental) + // + // Run vulnerability check (`govulncheck`). + RunVulncheckExp(context.Context, VulncheckArgs) error +} + +type RunTestsArgs struct { + // The test file containing the tests to run. + URI protocol.DocumentURI + + // Specific test names to run, e.g. TestFoo. + Tests []string + + // Specific benchmarks to run, e.g. BenchmarkFoo. + Benchmarks []string +} + +type GenerateArgs struct { + // URI for the directory to generate. + Dir protocol.DocumentURI + + // Whether to generate recursively (go generate ./...) + Recursive bool +} + +// TODO(rFindley): document the rest of these once the docgen is fleshed out. + +type ApplyFixArgs struct { + // The fix to apply. + Fix string + // The file URI for the document to fix. + URI protocol.DocumentURI + // The document range to scan for fixes. + Range protocol.Range +} + +type URIArg struct { + // The file URI. + URI protocol.DocumentURI +} + +type URIArgs struct { + // The file URIs. + URIs []protocol.DocumentURI +} + +type CheckUpgradesArgs struct { + // The go.mod file URI. + URI protocol.DocumentURI + // The modules to check. + Modules []string +} + +type DependencyArgs struct { + // The go.mod file URI. + URI protocol.DocumentURI + // Additional args to pass to the go command. + GoCmdArgs []string + // Whether to add a require directive. + AddRequire bool +} + +type RemoveDependencyArgs struct { + // The go.mod file URI. + URI protocol.DocumentURI + // The module path to remove. + ModulePath string + OnlyDiagnostic bool +} + +type EditGoDirectiveArgs struct { + // Any document URI within the relevant module. + URI protocol.DocumentURI + // The version to pass to `go mod edit -go`. + Version string +} + +type GoGetPackageArgs struct { + // Any document URI within the relevant module. + URI protocol.DocumentURI + // The package to go get. + Pkg string + AddRequire bool +} + +type AddImportArgs struct { + // ImportPath is the target import path that should + // be added to the URI file + ImportPath string + // URI is the file that the ImportPath should be + // added to + URI protocol.DocumentURI +} + +type ListKnownPackagesResult struct { + // Packages is a list of packages relative + // to the URIArg passed by the command request. + // In other words, it omits paths that are already + // imported or cannot be imported due to compiler + // restrictions. + Packages []string +} + +type ListImportsResult struct { + // Imports is a list of imports in the requested file. + Imports []FileImport + + // PackageImports is a list of all imports in the requested file's package. + PackageImports []PackageImport +} + +type FileImport struct { + // Path is the import path of the import. + Path string + // Name is the name of the import, e.g. `foo` in `import foo "strings"`. + Name string +} + +type PackageImport struct { + // Path is the import path of the import. + Path string +} + +type DebuggingArgs struct { + // Optional: the address (including port) for the debug server to listen on. + // If not provided, the debug server will bind to "localhost:0", and the + // full debug URL will be contained in the result. + // + // If there is more than one gopls instance along the serving path (i.e. you + // are using a daemon), each gopls instance will attempt to start debugging. + // If Addr specifies a port, only the daemon will be able to bind to that + // port, and each intermediate gopls instance will fail to start debugging. + // For this reason it is recommended not to specify a port (or equivalently, + // to specify ":0"). + // + // If the server was already debugging this field has no effect, and the + // result will contain the previously configured debug URL(s). + Addr string +} + +type DebuggingResult struct { + // The URLs to use to access the debug servers, for all gopls instances in + // the serving path. For the common case of a single gopls instance (i.e. no + // daemon), this will be exactly one address. + // + // In the case of one or more gopls instances forwarding the LSP to a daemon, + // URLs will contain debug addresses for each server in the serving path, in + // serving order. The daemon debug address will be the last entry in the + // slice. If any intermediate gopls instance fails to start debugging, no + // error will be returned but the debug URL for that server in the URLs slice + // will be empty. + URLs []string +} + +type VulncheckArgs struct { + // Any document in the directory from which govulncheck will run. + URI protocol.DocumentURI + + // Package pattern. E.g. "", ".", "./...". + Pattern string + + // TODO: -tests +} + +type VulncheckResult struct { + Vuln []Vuln + + // TODO: Text string format output? +} + +// CallStack models a trace of function calls starting +// with a client function or method and ending with a +// call to a vulnerable symbol. +type CallStack []StackEntry + +// StackEntry models an element of a call stack. +type StackEntry struct { + // See golang.org/x/exp/vulncheck.StackEntry. + + // User-friendly representation of function/method names. + // e.g. package.funcName, package.(recvType).methodName, ... + Name string + URI protocol.DocumentURI + Pos protocol.Position // Start position. (0-based. Column is always 0) +} + +// Vuln models an osv.Entry and representative call stacks. +// TODO: deprecate +type Vuln struct { + // ID is the vulnerability ID (osv.Entry.ID). + // https://ossf.github.io/osv-schema/#id-modified-fields + ID string + // Details is the description of the vulnerability (osv.Entry.Details). + // https://ossf.github.io/osv-schema/#summary-details-fields + Details string `json:",omitempty"` + // Aliases are alternative IDs of the vulnerability. + // https://ossf.github.io/osv-schema/#aliases-field + Aliases []string `json:",omitempty"` + + // Symbol is the name of the detected vulnerable function or method. + // Can be empty if the vulnerability exists in required modules, but no vulnerable symbols are used. + Symbol string `json:",omitempty"` + // PkgPath is the package path of the detected Symbol. + // Can be empty if the vulnerability exists in required modules, but no vulnerable packages are used. + PkgPath string `json:",omitempty"` + // ModPath is the module path corresponding to PkgPath. + // TODO: how do we specify standard library's vulnerability? + ModPath string `json:",omitempty"` + + // URL is the URL for more info about the information. + // Either the database specific URL or the one of the URLs + // included in osv.Entry.References. + URL string `json:",omitempty"` + + // Current is the current module version. + CurrentVersion string `json:",omitempty"` + + // Fixed is the minimum module version that contains the fix. + FixedVersion string `json:",omitempty"` + + // Example call stacks. + CallStacks []CallStack `json:",omitempty"` + + // Short description of each call stack in CallStacks. + CallStackSummaries []string `json:",omitempty"` + + // TODO: import graph & module graph. +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/command/util.go b/gopls/golang_org_x_tools_gopls/lsp/command/util.go new file mode 100644 index 0000000..011c341 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/command/util.go @@ -0,0 +1,63 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package command + +import ( + "encoding/json" + "fmt" +) + +// ID returns the command name for use in the LSP. +func ID(name string) string { + return "gopls." + name +} + +type Command string + +func (c Command) ID() string { + return ID(string(c)) +} + +// MarshalArgs encodes the given arguments to json.RawMessages. This function +// is used to construct arguments to a protocol.Command. +// +// Example usage: +// +// jsonArgs, err := MarshalArgs(1, "hello", true, StructuredArg{42, 12.6}) +func MarshalArgs(args ...interface{}) ([]json.RawMessage, error) { + var out []json.RawMessage + for _, arg := range args { + argJSON, err := json.Marshal(arg) + if err != nil { + return nil, err + } + out = append(out, argJSON) + } + return out, nil +} + +// UnmarshalArgs decodes the given json.RawMessages to the variables provided +// by args. Each element of args should be a pointer. +// +// Example usage: +// +// var ( +// num int +// str string +// bul bool +// structured StructuredArg +// ) +// err := UnmarshalArgs(args, &num, &str, &bul, &structured) +func UnmarshalArgs(jsonArgs []json.RawMessage, args ...interface{}) error { + if len(args) != len(jsonArgs) { + return fmt.Errorf("DecodeArgs: expected %d input arguments, got %d JSON arguments", len(args), len(jsonArgs)) + } + for i, arg := range args { + if err := json.Unmarshal(jsonArgs[i], arg); err != nil { + return err + } + } + return nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/lsppos/lsppos.go b/gopls/golang_org_x_tools_gopls/lsp/lsppos/lsppos.go new file mode 100644 index 0000000..702b822 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/lsppos/lsppos.go @@ -0,0 +1,134 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package lsppos provides utilities for working with LSP positions. Much of +// this functionality is duplicated from the internal/span package, but this +// package is simpler and more accurate with respect to newline terminated +// content. +// +// See https://microsoft.github.io/language-server-protocol/specification#textDocuments +// for a description of LSP positions. Notably: +// - Positions are specified by a 0-based line count and 0-based utf-16 +// character offset. +// - Positions are line-ending agnostic: there is no way to specify \r|\n or +// \n|. Instead the former maps to the end of the current line, and the +// latter to the start of the next line. +package lsppos + +import ( + "bytes" + "errors" + "sort" + "unicode/utf8" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +// Mapper maps utf-8 byte offsets to LSP positions for a single file. +type Mapper struct { + nonASCII bool + content []byte + + // Start-of-line positions. If src is newline-terminated, the final entry + // will be len(content). + lines []int +} + +// NewMapper creates a new Mapper for the given content. +func NewMapper(content []byte) *Mapper { + nlines := bytes.Count(content, []byte("\n")) + m := &Mapper{ + content: content, + lines: make([]int, 1, nlines+1), // initially []int{0} + } + for offset, b := range content { + if b == '\n' { + m.lines = append(m.lines, offset+1) + } + if b >= utf8.RuneSelf { + m.nonASCII = true + } + } + return m +} + +// LineColUTF16 returns the 0-based UTF-16 line and character index for the +// given offset. It returns -1, -1 if offset is out of bounds for the file +// being mapped. +func (m *Mapper) LineColUTF16(offset int) (line, char int) { + if offset < 0 || offset > len(m.content) { + return -1, -1 + } + nextLine := sort.Search(len(m.lines), func(i int) bool { + return offset < m.lines[i] + }) + if nextLine == 0 { + return -1, -1 + } + line = nextLine - 1 + start := m.lines[line] + var charOffset int + if m.nonASCII { + charOffset = UTF16len(m.content[start:offset]) + } else { + charOffset = offset - start + } + + var eol int + if line == len(m.lines)-1 { + eol = len(m.content) + } else { + eol = m.lines[line+1] - 1 + } + + // Adjustment for line-endings: \r|\n is the same as |\r\n. + if offset == eol && offset > 0 && m.content[offset-1] == '\r' { + charOffset-- + } + + return line, charOffset +} + +// Position returns the protocol position corresponding to the given offset. It +// returns false if offset is out of bounds for the file being mapped. +func (m *Mapper) Position(offset int) (protocol.Position, bool) { + l, c := m.LineColUTF16(offset) + if l < 0 { + return protocol.Position{}, false + } + return protocol.Position{ + Line: uint32(l), + Character: uint32(c), + }, true +} + +// Range returns the protocol range corresponding to the given start and end +// offsets. +func (m *Mapper) Range(start, end int) (protocol.Range, error) { + startPos, ok := m.Position(start) + if !ok { + return protocol.Range{}, errors.New("invalid start position") + } + endPos, ok := m.Position(end) + if !ok { + return protocol.Range{}, errors.New("invalid end position") + } + + return protocol.Range{Start: startPos, End: endPos}, nil +} + +// UTF16len returns the UTF-16 length of the UTF-8 encoded content, were it to +// be re-encoded as UTF-16. +func UTF16len(buf []byte) int { + // This function copies buf, but microbenchmarks showed it to be faster than + // using utf8.DecodeRune due to inlining and avoiding bounds checks. + cnt := 0 + for _, r := range string(buf) { + cnt++ + if r >= 1<<16 { + cnt++ + } + } + return cnt +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/lsppos/token.go b/gopls/golang_org_x_tools_gopls/lsp/lsppos/token.go new file mode 100644 index 0000000..58b7fdc --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/lsppos/token.go @@ -0,0 +1,67 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lsppos + +import ( + "errors" + "go/ast" + "go/token" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" +) + +// TokenMapper maps token.Pos to LSP positions for a single file. +type TokenMapper struct { + // file is used for computing offsets. + file *token.File + + // For now, just delegate to a Mapper for position calculation. As an + // optimization we could avoid building the mapper and just use the file, but + // then have to correctly adjust for newline-terminated files. It is easier + // to just delegate unless performance becomes a concern. + mapper *Mapper +} + +// NewTokenMapper creates a new TokenMapper for the given content, using the +// provided file to compute offsets. +func NewTokenMapper(content []byte, file *token.File) *TokenMapper { + return &TokenMapper{ + file: file, + mapper: NewMapper(content), + } +} + +// Position returns the protocol position corresponding to the given pos. It +// returns false if pos is out of bounds for the file being mapped. +func (m *TokenMapper) Position(pos token.Pos) (protocol.Position, bool) { + offset, err := safetoken.Offset(m.file, pos) + if err != nil { + return protocol.Position{}, false + } + return m.mapper.Position(offset) +} + +// Range returns the protocol range corresponding to the given start and end +// positions. It returns an error if start or end is out of bounds for the file +// being mapped. +func (m *TokenMapper) Range(start, end token.Pos) (protocol.Range, error) { + startPos, ok := m.Position(start) + if !ok { + return protocol.Range{}, errors.New("invalid start position") + } + endPos, ok := m.Position(end) + if !ok { + return protocol.Range{}, errors.New("invalid end position") + } + + return protocol.Range{Start: startPos, End: endPos}, nil +} + +// NodeRange returns the protocol range corresponding to the span of the given +// node. +func (m *TokenMapper) NodeRange(n ast.Node) (protocol.Range, error) { + return m.Range(n.Pos(), n.End()) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/codeactionkind.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/codeactionkind.go new file mode 100644 index 0000000..9a95800 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/codeactionkind.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +// Custom code actions that aren't explicitly stated in LSP +const ( + GoTest CodeActionKind = "goTest" + // TODO: Add GoGenerate, RegenerateCgo etc. +) diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/context.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/context.go new file mode 100644 index 0000000..bd13126 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/context.go @@ -0,0 +1,43 @@ +// 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 protocol + +import ( + "bytes" + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/core" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/export" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/label" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/xcontext" +) + +type contextKey int + +const ( + clientKey = contextKey(iota) +) + +func WithClient(ctx context.Context, client Client) context.Context { + return context.WithValue(ctx, clientKey, client) +} + +func LogEvent(ctx context.Context, ev core.Event, lm label.Map, mt MessageType) context.Context { + client, ok := ctx.Value(clientKey).(Client) + if !ok { + return ctx + } + buf := &bytes.Buffer{} + p := export.Printer{} + p.WriteEvent(buf, ev, lm) + msg := &LogMessageParams{Type: mt, Message: buf.String()} + // Handle messages generated via event.Error, which won't have a level Label. + if event.IsError(ev) { + msg.Type = Error + } + go client.LogMessage(xcontext.Detach(ctx), msg) + return ctx +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/doc.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/doc.go new file mode 100644 index 0000000..2ffdf51 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/doc.go @@ -0,0 +1,16 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package protocol contains the structs that map directly to the wire format +// of the "Language Server Protocol". +// +// It is a literal transcription, with unmodified comments, and only the changes +// required to make it go code. +// Names are uppercased to export them. +// All fields have JSON tags added to correct the names. +// Fields marked with a ? are also marked as "omitempty" +// Fields that are "|| null" are made pointers +// Fields that are string or number are left as string +// Fields that are type "number" are made float64 +package protocol diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/enums.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/enums.go new file mode 100644 index 0000000..82398e2 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/enums.go @@ -0,0 +1,231 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +import ( + "fmt" +) + +var ( + namesTextDocumentSyncKind [int(Incremental) + 1]string + namesMessageType [int(Log) + 1]string + namesFileChangeType [int(Deleted) + 1]string + namesWatchKind [int(WatchDelete) + 1]string + namesCompletionTriggerKind [int(TriggerForIncompleteCompletions) + 1]string + namesDiagnosticSeverity [int(SeverityHint) + 1]string + namesDiagnosticTag [int(Unnecessary) + 1]string + namesCompletionItemKind [int(TypeParameterCompletion) + 1]string + namesInsertTextFormat [int(SnippetTextFormat) + 1]string + namesDocumentHighlightKind [int(Write) + 1]string + namesSymbolKind [int(TypeParameter) + 1]string + namesTextDocumentSaveReason [int(FocusOut) + 1]string +) + +func init() { + namesTextDocumentSyncKind[int(None)] = "None" + namesTextDocumentSyncKind[int(Full)] = "Full" + namesTextDocumentSyncKind[int(Incremental)] = "Incremental" + + namesMessageType[int(Error)] = "Error" + namesMessageType[int(Warning)] = "Warning" + namesMessageType[int(Info)] = "Info" + namesMessageType[int(Log)] = "Log" + + namesFileChangeType[int(Created)] = "Created" + namesFileChangeType[int(Changed)] = "Changed" + namesFileChangeType[int(Deleted)] = "Deleted" + + namesWatchKind[int(WatchCreate)] = "WatchCreate" + namesWatchKind[int(WatchChange)] = "WatchChange" + namesWatchKind[int(WatchDelete)] = "WatchDelete" + + namesCompletionTriggerKind[int(Invoked)] = "Invoked" + namesCompletionTriggerKind[int(TriggerCharacter)] = "TriggerCharacter" + namesCompletionTriggerKind[int(TriggerForIncompleteCompletions)] = "TriggerForIncompleteCompletions" + + namesDiagnosticSeverity[int(SeverityError)] = "Error" + namesDiagnosticSeverity[int(SeverityWarning)] = "Warning" + namesDiagnosticSeverity[int(SeverityInformation)] = "Information" + namesDiagnosticSeverity[int(SeverityHint)] = "Hint" + + namesDiagnosticTag[int(Unnecessary)] = "Unnecessary" + + namesCompletionItemKind[int(TextCompletion)] = "text" + namesCompletionItemKind[int(MethodCompletion)] = "method" + namesCompletionItemKind[int(FunctionCompletion)] = "func" + namesCompletionItemKind[int(ConstructorCompletion)] = "constructor" + namesCompletionItemKind[int(FieldCompletion)] = "field" + namesCompletionItemKind[int(VariableCompletion)] = "var" + namesCompletionItemKind[int(ClassCompletion)] = "type" + namesCompletionItemKind[int(InterfaceCompletion)] = "interface" + namesCompletionItemKind[int(ModuleCompletion)] = "package" + namesCompletionItemKind[int(PropertyCompletion)] = "property" + namesCompletionItemKind[int(UnitCompletion)] = "unit" + namesCompletionItemKind[int(ValueCompletion)] = "value" + namesCompletionItemKind[int(EnumCompletion)] = "enum" + namesCompletionItemKind[int(KeywordCompletion)] = "keyword" + namesCompletionItemKind[int(SnippetCompletion)] = "snippet" + namesCompletionItemKind[int(ColorCompletion)] = "color" + namesCompletionItemKind[int(FileCompletion)] = "file" + namesCompletionItemKind[int(ReferenceCompletion)] = "reference" + namesCompletionItemKind[int(FolderCompletion)] = "folder" + namesCompletionItemKind[int(EnumMemberCompletion)] = "enumMember" + namesCompletionItemKind[int(ConstantCompletion)] = "const" + namesCompletionItemKind[int(StructCompletion)] = "struct" + namesCompletionItemKind[int(EventCompletion)] = "event" + namesCompletionItemKind[int(OperatorCompletion)] = "operator" + namesCompletionItemKind[int(TypeParameterCompletion)] = "typeParam" + + namesInsertTextFormat[int(PlainTextTextFormat)] = "PlainText" + namesInsertTextFormat[int(SnippetTextFormat)] = "Snippet" + + namesDocumentHighlightKind[int(Text)] = "Text" + namesDocumentHighlightKind[int(Read)] = "Read" + namesDocumentHighlightKind[int(Write)] = "Write" + + namesSymbolKind[int(File)] = "File" + namesSymbolKind[int(Module)] = "Module" + namesSymbolKind[int(Namespace)] = "Namespace" + namesSymbolKind[int(Package)] = "Package" + namesSymbolKind[int(Class)] = "Class" + namesSymbolKind[int(Method)] = "Method" + namesSymbolKind[int(Property)] = "Property" + namesSymbolKind[int(Field)] = "Field" + namesSymbolKind[int(Constructor)] = "Constructor" + namesSymbolKind[int(Enum)] = "Enum" + namesSymbolKind[int(Interface)] = "Interface" + namesSymbolKind[int(Function)] = "Function" + namesSymbolKind[int(Variable)] = "Variable" + namesSymbolKind[int(Constant)] = "Constant" + namesSymbolKind[int(String)] = "String" + namesSymbolKind[int(Number)] = "Number" + namesSymbolKind[int(Boolean)] = "Boolean" + namesSymbolKind[int(Array)] = "Array" + namesSymbolKind[int(Object)] = "Object" + namesSymbolKind[int(Key)] = "Key" + namesSymbolKind[int(Null)] = "Null" + namesSymbolKind[int(EnumMember)] = "EnumMember" + namesSymbolKind[int(Struct)] = "Struct" + namesSymbolKind[int(Event)] = "Event" + namesSymbolKind[int(Operator)] = "Operator" + namesSymbolKind[int(TypeParameter)] = "TypeParameter" + + namesTextDocumentSaveReason[int(Manual)] = "Manual" + namesTextDocumentSaveReason[int(AfterDelay)] = "AfterDelay" + namesTextDocumentSaveReason[int(FocusOut)] = "FocusOut" +} + +func formatEnum(f fmt.State, c rune, i int, names []string, unknown string) { + s := "" + if i >= 0 && i < len(names) { + s = names[i] + } + if s != "" { + fmt.Fprint(f, s) + } else { + fmt.Fprintf(f, "%s(%d)", unknown, i) + } +} + +func parseEnum(s string, names []string) int { + for i, name := range names { + if s == name { + return i + } + } + return 0 +} + +func (e TextDocumentSyncKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesTextDocumentSyncKind[:], "TextDocumentSyncKind") +} + +func ParseTextDocumentSyncKind(s string) TextDocumentSyncKind { + return TextDocumentSyncKind(parseEnum(s, namesTextDocumentSyncKind[:])) +} + +func (e MessageType) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesMessageType[:], "MessageType") +} + +func ParseMessageType(s string) MessageType { + return MessageType(parseEnum(s, namesMessageType[:])) +} + +func (e FileChangeType) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesFileChangeType[:], "FileChangeType") +} + +func ParseFileChangeType(s string) FileChangeType { + return FileChangeType(parseEnum(s, namesFileChangeType[:])) +} + +func ParseWatchKind(s string) WatchKind { + return WatchKind(parseEnum(s, namesWatchKind[:])) +} + +func (e CompletionTriggerKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesCompletionTriggerKind[:], "CompletionTriggerKind") +} + +func ParseCompletionTriggerKind(s string) CompletionTriggerKind { + return CompletionTriggerKind(parseEnum(s, namesCompletionTriggerKind[:])) +} + +func (e DiagnosticSeverity) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDiagnosticSeverity[:], "DiagnosticSeverity") +} + +func ParseDiagnosticSeverity(s string) DiagnosticSeverity { + return DiagnosticSeverity(parseEnum(s, namesDiagnosticSeverity[:])) +} + +func (e DiagnosticTag) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDiagnosticTag[:], "DiagnosticTag") +} + +func ParseDiagnosticTag(s string) DiagnosticTag { + return DiagnosticTag(parseEnum(s, namesDiagnosticTag[:])) +} + +func (e CompletionItemKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesCompletionItemKind[:], "CompletionItemKind") +} + +func ParseCompletionItemKind(s string) CompletionItemKind { + return CompletionItemKind(parseEnum(s, namesCompletionItemKind[:])) +} + +func (e InsertTextFormat) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesInsertTextFormat[:], "InsertTextFormat") +} + +func ParseInsertTextFormat(s string) InsertTextFormat { + return InsertTextFormat(parseEnum(s, namesInsertTextFormat[:])) +} + +func (e DocumentHighlightKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDocumentHighlightKind[:], "DocumentHighlightKind") +} + +func ParseDocumentHighlightKind(s string) DocumentHighlightKind { + return DocumentHighlightKind(parseEnum(s, namesDocumentHighlightKind[:])) +} + +func (e SymbolKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesSymbolKind[:], "SymbolKind") +} + +func ParseSymbolKind(s string) SymbolKind { + return SymbolKind(parseEnum(s, namesSymbolKind[:])) +} + +func (e TextDocumentSaveReason) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesTextDocumentSaveReason[:], "TextDocumentSaveReason") +} + +func ParseTextDocumentSaveReason(s string) TextDocumentSaveReason { + return TextDocumentSaveReason(parseEnum(s, namesTextDocumentSaveReason[:])) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/log.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/log.go new file mode 100644 index 0000000..7856b3a --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/log.go @@ -0,0 +1,136 @@ +// 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 protocol + +import ( + "context" + "fmt" + "io" + "strings" + "sync" + "time" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/jsonrpc2" +) + +type loggingStream struct { + stream jsonrpc2.Stream + logMu sync.Mutex + log io.Writer +} + +// LoggingStream returns a stream that does LSP protocol logging too +func LoggingStream(str jsonrpc2.Stream, w io.Writer) jsonrpc2.Stream { + return &loggingStream{stream: str, log: w} +} + +func (s *loggingStream) Read(ctx context.Context) (jsonrpc2.Message, int64, error) { + msg, count, err := s.stream.Read(ctx) + if err == nil { + s.logCommon(msg, true) + } + return msg, count, err +} + +func (s *loggingStream) Write(ctx context.Context, msg jsonrpc2.Message) (int64, error) { + s.logCommon(msg, false) + count, err := s.stream.Write(ctx, msg) + return count, err +} + +func (s *loggingStream) Close() error { + return s.stream.Close() +} + +type req struct { + method string + start time.Time +} + +type mapped struct { + mu sync.Mutex + clientCalls map[string]req + serverCalls map[string]req +} + +var maps = &mapped{ + sync.Mutex{}, + make(map[string]req), + make(map[string]req), +} + +// these 4 methods are each used exactly once, but it seemed +// better to have the encapsulation rather than ad hoc mutex +// code in 4 places +func (m *mapped) client(id string) req { + m.mu.Lock() + defer m.mu.Unlock() + v := m.clientCalls[id] + delete(m.clientCalls, id) + return v +} + +func (m *mapped) server(id string) req { + m.mu.Lock() + defer m.mu.Unlock() + v := m.serverCalls[id] + delete(m.serverCalls, id) + return v +} + +func (m *mapped) setClient(id string, r req) { + m.mu.Lock() + defer m.mu.Unlock() + m.clientCalls[id] = r +} + +func (m *mapped) setServer(id string, r req) { + m.mu.Lock() + defer m.mu.Unlock() + m.serverCalls[id] = r +} + +const eor = "\r\n\r\n\r\n" + +func (s *loggingStream) logCommon(msg jsonrpc2.Message, isRead bool) { + s.logMu.Lock() + defer s.logMu.Unlock() + direction, pastTense := "Received", "Received" + get, set := maps.client, maps.setServer + if isRead { + direction, pastTense = "Sending", "Sent" + get, set = maps.server, maps.setClient + } + if msg == nil || s.log == nil { + return + } + tm := time.Now() + tmfmt := tm.Format("15:04:05.000 PM") + + buf := strings.Builder{} + fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning + switch msg := msg.(type) { + case *jsonrpc2.Call: + id := fmt.Sprint(msg.ID()) + fmt.Fprintf(&buf, "%s request '%s - (%s)'.\n", direction, msg.Method(), id) + fmt.Fprintf(&buf, "Params: %s%s", msg.Params(), eor) + set(id, req{method: msg.Method(), start: tm}) + case *jsonrpc2.Notification: + fmt.Fprintf(&buf, "%s notification '%s'.\n", direction, msg.Method()) + fmt.Fprintf(&buf, "Params: %s%s", msg.Params(), eor) + case *jsonrpc2.Response: + id := fmt.Sprint(msg.ID()) + if err := msg.Err(); err != nil { + fmt.Fprintf(s.log, "[Error - %s] %s #%s %s%s", pastTense, tmfmt, id, err, eor) + return + } + cc := get(id) + elapsed := tm.Sub(cc.start) + fmt.Fprintf(&buf, "%s response '%s - (%s)' in %dms.\n", + direction, cc.method, id, elapsed/time.Millisecond) + fmt.Fprintf(&buf, "Result: %s%s", msg.Result(), eor) + } + s.log.Write([]byte(buf.String())) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/protocol.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/protocol.go new file mode 100644 index 0000000..acba7f3 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/protocol.go @@ -0,0 +1,284 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/jsonrpc2" + jsonrpc2_v2 "github.com/visualfc/gotools/gopls/golang_org_x_tools/jsonrpc2_v2" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/xcontext" +) + +var ( + // RequestCancelledError should be used when a request is cancelled early. + RequestCancelledError = jsonrpc2.NewError(-32800, "JSON RPC cancelled") + RequestCancelledErrorV2 = jsonrpc2_v2.NewError(-32800, "JSON RPC cancelled") +) + +type ClientCloser interface { + Client + io.Closer +} + +type connSender interface { + io.Closer + + Notify(ctx context.Context, method string, params interface{}) error + Call(ctx context.Context, method string, params, result interface{}) error +} + +type clientDispatcher struct { + sender connSender +} + +func (c *clientDispatcher) Close() error { + return c.sender.Close() +} + +// ClientDispatcher returns a Client that dispatches LSP requests across the +// given jsonrpc2 connection. +func ClientDispatcher(conn jsonrpc2.Conn) ClientCloser { + return &clientDispatcher{sender: clientConn{conn}} +} + +type clientConn struct { + conn jsonrpc2.Conn +} + +func (c clientConn) Close() error { + return c.conn.Close() +} + +func (c clientConn) Notify(ctx context.Context, method string, params interface{}) error { + return c.conn.Notify(ctx, method, params) +} + +func (c clientConn) Call(ctx context.Context, method string, params interface{}, result interface{}) error { + id, err := c.conn.Call(ctx, method, params, result) + if ctx.Err() != nil { + cancelCall(ctx, c, id) + } + return err +} + +func ClientDispatcherV2(conn *jsonrpc2_v2.Connection) ClientCloser { + return &clientDispatcher{clientConnV2{conn}} +} + +type clientConnV2 struct { + conn *jsonrpc2_v2.Connection +} + +func (c clientConnV2) Close() error { + return c.conn.Close() +} + +func (c clientConnV2) Notify(ctx context.Context, method string, params interface{}) error { + return c.conn.Notify(ctx, method, params) +} + +func (c clientConnV2) Call(ctx context.Context, method string, params interface{}, result interface{}) error { + call := c.conn.Call(ctx, method, params) + err := call.Await(ctx, result) + if ctx.Err() != nil { + detached := xcontext.Detach(ctx) + c.conn.Notify(detached, "$/cancelRequest", &CancelParams{ID: call.ID().Raw()}) + } + return err +} + +// ServerDispatcher returns a Server that dispatches LSP requests across the +// given jsonrpc2 connection. +func ServerDispatcher(conn jsonrpc2.Conn) Server { + return &serverDispatcher{sender: clientConn{conn}} +} + +func ServerDispatcherV2(conn *jsonrpc2_v2.Connection) Server { + return &serverDispatcher{sender: clientConnV2{conn}} +} + +type serverDispatcher struct { + sender connSender +} + +func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler { + return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { + if ctx.Err() != nil { + ctx := xcontext.Detach(ctx) + return reply(ctx, nil, RequestCancelledError) + } + handled, err := clientDispatch(ctx, client, reply, req) + if handled || err != nil { + return err + } + return handler(ctx, reply, req) + } +} + +func ClientHandlerV2(client Client) jsonrpc2_v2.Handler { + return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { + if ctx.Err() != nil { + return nil, RequestCancelledErrorV2 + } + req1 := req2to1(req) + var ( + result interface{} + resErr error + ) + replier := func(_ context.Context, res interface{}, err error) error { + if err != nil { + resErr = err + return nil + } + result = res + return nil + } + _, err := clientDispatch(ctx, client, replier, req1) + if err != nil { + return nil, err + } + return result, resErr + }) +} + +func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler { + return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { + if ctx.Err() != nil { + ctx := xcontext.Detach(ctx) + return reply(ctx, nil, RequestCancelledError) + } + handled, err := serverDispatch(ctx, server, reply, req) + if handled || err != nil { + return err + } + //TODO: This code is wrong, it ignores handler and assumes non standard + // request handles everything + // non standard request should just be a layered handler. + var params interface{} + if err := json.Unmarshal(req.Params(), ¶ms); err != nil { + return sendParseError(ctx, reply, err) + } + resp, err := server.NonstandardRequest(ctx, req.Method(), params) + return reply(ctx, resp, err) + + } +} + +func ServerHandlerV2(server Server) jsonrpc2_v2.Handler { + return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { + if ctx.Err() != nil { + return nil, RequestCancelledErrorV2 + } + req1 := req2to1(req) + var ( + result interface{} + resErr error + ) + replier := func(_ context.Context, res interface{}, err error) error { + if err != nil { + resErr = err + return nil + } + result = res + return nil + } + _, err := serverDispatch(ctx, server, replier, req1) + if err != nil { + return nil, err + } + return result, resErr + }) +} + +func req2to1(req2 *jsonrpc2_v2.Request) jsonrpc2.Request { + if req2.ID.IsValid() { + raw := req2.ID.Raw() + var idv1 jsonrpc2.ID + switch v := raw.(type) { + case int64: + idv1 = jsonrpc2.NewIntID(v) + case string: + idv1 = jsonrpc2.NewStringID(v) + default: + panic(fmt.Sprintf("unsupported ID type %T", raw)) + } + req1, err := jsonrpc2.NewCall(idv1, req2.Method, req2.Params) + if err != nil { + panic(err) + } + return req1 + } + req1, err := jsonrpc2.NewNotification(req2.Method, req2.Params) + if err != nil { + panic(err) + } + return req1 +} + +func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler { + return CancelHandler( + jsonrpc2.AsyncHandler( + jsonrpc2.MustReplyHandler(handler))) +} + +func CancelHandler(handler jsonrpc2.Handler) jsonrpc2.Handler { + handler, canceller := jsonrpc2.CancelHandler(handler) + return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { + if req.Method() != "$/cancelRequest" { + // TODO(iancottrell): See if we can generate a reply for the request to be cancelled + // at the point of cancellation rather than waiting for gopls to naturally reply. + // To do that, we need to keep track of whether a reply has been sent already and + // be careful about racing between the two paths. + // TODO(iancottrell): Add a test that watches the stream and verifies the response + // for the cancelled request flows. + replyWithDetachedContext := func(ctx context.Context, resp interface{}, err error) error { + // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest + if ctx.Err() != nil && err == nil { + err = RequestCancelledError + } + ctx = xcontext.Detach(ctx) + return reply(ctx, resp, err) + } + return handler(ctx, replyWithDetachedContext, req) + } + var params CancelParams + if err := json.Unmarshal(req.Params(), ¶ms); err != nil { + return sendParseError(ctx, reply, err) + } + if n, ok := params.ID.(float64); ok { + canceller(jsonrpc2.NewIntID(int64(n))) + } else if s, ok := params.ID.(string); ok { + canceller(jsonrpc2.NewStringID(s)) + } else { + return sendParseError(ctx, reply, fmt.Errorf("request ID %v malformed", params.ID)) + } + return reply(ctx, nil, nil) + } +} + +func Call(ctx context.Context, conn jsonrpc2.Conn, method string, params interface{}, result interface{}) error { + id, err := conn.Call(ctx, method, params, result) + if ctx.Err() != nil { + cancelCall(ctx, clientConn{conn}, id) + } + return err +} + +func cancelCall(ctx context.Context, sender connSender, id jsonrpc2.ID) { + ctx = xcontext.Detach(ctx) + ctx, done := event.Start(ctx, "protocol.canceller") + defer done() + // Note that only *jsonrpc2.ID implements json.Marshaler. + sender.Notify(ctx, "$/cancelRequest", &CancelParams{ID: &id}) +} + +func sendParseError(ctx context.Context, reply jsonrpc2.Replier, err error) error { + return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err)) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/span.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/span.go new file mode 100644 index 0000000..f50794c --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/span.go @@ -0,0 +1,318 @@ +// 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. + +// this file contains protocol<->span converters + +// Here's a handy guide for your tour of the location zoo: +// +// Imports: source --> lsppos --> protocol --> span --> token +// +// source.MappedRange = (span.Range, protocol.ColumnMapper) +// +// lsppos.TokenMapper = (token.File, lsppos.Mapper) +// lsppos.Mapper = (line offset table, content) +// +// protocol.ColumnMapper = (URI, token.File, content) +// protocol.Location = (URI, protocol.Range) +// protocol.Range = (start, end Position) +// protocol.Position = (line, char uint32) 0-based UTF-16 +// +// span.Point = (line?, col?, offset?) 1-based UTF-8 +// span.Span = (uri URI, start, end span.Point) +// span.Range = (file token.File, start, end token.Pos) +// +// token.Pos +// token.FileSet +// offset int +// +// TODO(adonovan): simplify this picture. Eliminate the optionality of +// span.{Span,Point}'s position and offset fields: work internally in +// terms of offsets (like span.Range), and require a mapper to convert +// them to protocol (UTF-16) line/col form. Stop honoring //line +// directives. + +package protocol + +import ( + "bytes" + "fmt" + "go/token" + "unicode/utf8" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" +) + +// A ColumnMapper maps between UTF-8 oriented positions (e.g. token.Pos, +// span.Span) and the UTF-16 oriented positions used by the LSP. +type ColumnMapper struct { + URI span.URI + TokFile *token.File + Content []byte + + // File content is only really needed for UTF-16 column + // computation, which could be be achieved more compactly. + // For example, one could record only the lines for which + // UTF-16 columns differ from the UTF-8 ones, or only the + // indices of the non-ASCII characters. + // + // TODO(adonovan): consider not retaining the entire file + // content, or at least not exposing the fact that we + // currently retain it. +} + +// NewColumnMapper creates a new column mapper for the given uri and content. +func NewColumnMapper(uri span.URI, content []byte) *ColumnMapper { + tf := span.NewTokenFile(uri.Filename(), content) + return &ColumnMapper{ + URI: uri, + TokFile: tf, + Content: content, + } +} + +func URIFromSpanURI(uri span.URI) DocumentURI { + return DocumentURI(uri) +} + +func URIFromPath(path string) DocumentURI { + return URIFromSpanURI(span.URIFromPath(path)) +} + +func (u DocumentURI) SpanURI() span.URI { + return span.URIFromURI(string(u)) +} + +func (m *ColumnMapper) Location(s span.Span) (Location, error) { + rng, err := m.Range(s) + if err != nil { + return Location{}, err + } + return Location{URI: URIFromSpanURI(s.URI()), Range: rng}, nil +} + +func (m *ColumnMapper) Range(s span.Span) (Range, error) { + if span.CompareURI(m.URI, s.URI()) != 0 { + return Range{}, fmt.Errorf("column mapper is for file %q instead of %q", m.URI, s.URI()) + } + s, err := s.WithOffset(m.TokFile) + if err != nil { + return Range{}, err + } + start, err := m.Position(s.Start()) + if err != nil { + return Range{}, err + } + end, err := m.Position(s.End()) + if err != nil { + return Range{}, err + } + return Range{Start: start, End: end}, nil +} + +// OffsetRange returns a Range for the byte-offset interval Content[start:end], +func (m *ColumnMapper) OffsetRange(start, end int) (Range, error) { + startPosition, err := m.OffsetPosition(start) + if err != nil { + return Range{}, fmt.Errorf("start: %v", err) + } + + endPosition, err := m.OffsetPosition(end) + if err != nil { + return Range{}, fmt.Errorf("end: %v", err) + } + + return Range{Start: startPosition, End: endPosition}, nil +} + +// PosRange returns a protocol Range for the token.Pos interval Content[start:end]. +func (m *ColumnMapper) PosRange(start, end token.Pos) (Range, error) { + startOffset, err := safetoken.Offset(m.TokFile, start) + if err != nil { + return Range{}, fmt.Errorf("start: %v", err) + } + endOffset, err := safetoken.Offset(m.TokFile, end) + if err != nil { + return Range{}, fmt.Errorf("end: %v", err) + } + return m.OffsetRange(startOffset, endOffset) +} + +// Position returns the protocol position for the specified point, +// which must have a byte offset. +func (m *ColumnMapper) Position(p span.Point) (Position, error) { + if !p.HasOffset() { + return Position{}, fmt.Errorf("point is missing offset") + } + return m.OffsetPosition(p.Offset()) +} + +// OffsetPosition returns the protocol position of the specified +// offset within m.Content. +func (m *ColumnMapper) OffsetPosition(offset int) (Position, error) { + // We use span.ToPosition for its "line+1 at EOF" workaround. + // TODO(adonovan): ToPosition honors //line directives. It probably shouldn't. + line, _, err := span.ToPosition(m.TokFile, offset) + if err != nil { + return Position{}, fmt.Errorf("OffsetPosition: %v", err) + } + // If that workaround executed, skip the usual column computation. + char := 0 + if offset != m.TokFile.Size() { + char = m.utf16Column(offset) + } + return Position{ + Line: uint32(line - 1), + Character: uint32(char), + }, nil +} + +// utf16Column returns the zero-based column index of the +// specified file offset, measured in UTF-16 codes. +// Precondition: 0 <= offset <= len(m.Content). +func (m *ColumnMapper) utf16Column(offset int) int { + s := m.Content[:offset] + if i := bytes.LastIndex(s, []byte("\n")); i >= 0 { + s = s[i+1:] + } + // s is the prefix of the line before offset. + return utf16len(s) +} + +// utf16len returns the number of codes in the UTF-16 transcoding of s. +func utf16len(s []byte) int { + var n int + for len(s) > 0 { + n++ + + // Fast path for ASCII. + if s[0] < 0x80 { + s = s[1:] + continue + } + + r, size := utf8.DecodeRune(s) + if r >= 0x10000 { + n++ // surrogate pair + } + s = s[size:] + } + return n +} + +func (m *ColumnMapper) Span(l Location) (span.Span, error) { + return m.RangeSpan(l.Range) +} + +// RangeSpan converts a UTF-16 range to a Span with both the +// position (line/col) and offset fields populated. +func (m *ColumnMapper) RangeSpan(r Range) (span.Span, error) { + start, err := m.Point(r.Start) + if err != nil { + return span.Span{}, err + } + end, err := m.Point(r.End) + if err != nil { + return span.Span{}, err + } + return span.New(m.URI, start, end).WithAll(m.TokFile) +} + +func (m *ColumnMapper) RangeToSpanRange(r Range) (span.Range, error) { + spn, err := m.RangeSpan(r) + if err != nil { + return span.Range{}, err + } + return spn.Range(m.TokFile) +} + +// Pos returns the token.Pos of protocol position p within the mapped file. +func (m *ColumnMapper) Pos(p Position) (token.Pos, error) { + start, err := m.Point(p) + if err != nil { + return token.NoPos, err + } + return safetoken.Pos(m.TokFile, start.Offset()) +} + +// Offset returns the utf-8 byte offset of p within the mapped file. +func (m *ColumnMapper) Offset(p Position) (int, error) { + start, err := m.Point(p) + if err != nil { + return 0, err + } + return start.Offset(), nil +} + +// Point returns a span.Point for the protocol position p within the mapped file. +// The resulting point has a valid Position and Offset. +func (m *ColumnMapper) Point(p Position) (span.Point, error) { + line := int(p.Line) + 1 + + // Find byte offset of start of containing line. + offset, err := span.ToOffset(m.TokFile, line, 1) + if err != nil { + return span.Point{}, err + } + lineStart := span.NewPoint(line, 1, offset) + return span.FromUTF16Column(lineStart, int(p.Character)+1, m.Content) +} + +func IsPoint(r Range) bool { + return r.Start.Line == r.End.Line && r.Start.Character == r.End.Character +} + +// CompareRange returns -1 if a is before b, 0 if a == b, and 1 if a is after +// b. +// +// A range a is defined to be 'before' b if a.Start is before b.Start, or +// a.Start == b.Start and a.End is before b.End. +func CompareRange(a, b Range) int { + if r := ComparePosition(a.Start, b.Start); r != 0 { + return r + } + return ComparePosition(a.End, b.End) +} + +// ComparePosition returns -1 if a is before b, 0 if a == b, and 1 if a is +// after b. +func ComparePosition(a, b Position) int { + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Character < b.Character { + return -1 + } + if a.Character > b.Character { + return 1 + } + return 0 +} + +func Intersect(a, b Range) bool { + if a.Start.Line > b.End.Line || a.End.Line < b.Start.Line { + return false + } + return !((a.Start.Line == b.End.Line) && a.Start.Character > b.End.Character || + (a.End.Line == b.Start.Line) && a.End.Character < b.Start.Character) +} + +// Format implements fmt.Formatter. +// +// Note: Formatter is implemented instead of Stringer (presumably) for +// performance reasons, though it is not clear that it matters in practice. +func (r Range) Format(f fmt.State, _ rune) { + fmt.Fprintf(f, "%v-%v", r.Start, r.End) +} + +// Format implements fmt.Formatter. +// +// See Range.Format for discussion of why the Formatter interface is +// implemented rather than Stringer. +func (p Position) Format(f fmt.State, _ rune) { + fmt.Fprintf(f, "%v:%v", p.Line, p.Character) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/tsclient.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsclient.go new file mode 100644 index 0000000..54a1207 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsclient.go @@ -0,0 +1,219 @@ +// Copyright 2019-2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +// Code generated from version 3.17.0 of protocol/metaModel.json. +// git hash 8de18faed635819dd2bc631d2c26ce4a18f7cf4a (as of Fri Sep 16 13:04:31 2022) +// Code generated; DO NOT EDIT. + +import ( + "context" + "encoding/json" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/jsonrpc2" +) + +type Client interface { + LogTrace(context.Context, *LogTraceParams) error // $/logTrace + Progress(context.Context, *ProgressParams) error // $/progress + RegisterCapability(context.Context, *RegistrationParams) error // client/registerCapability + UnregisterCapability(context.Context, *UnregistrationParams) error // client/unregisterCapability + Event(context.Context, *interface{}) error // telemetry/event + PublishDiagnostics(context.Context, *PublishDiagnosticsParams) error // textDocument/publishDiagnostics + LogMessage(context.Context, *LogMessageParams) error // window/logMessage + ShowDocument(context.Context, *ShowDocumentParams) (*ShowDocumentResult, error) // window/showDocument + ShowMessage(context.Context, *ShowMessageParams) error // window/showMessage + ShowMessageRequest(context.Context, *ShowMessageRequestParams) (*MessageActionItem, error) // window/showMessageRequest + WorkDoneProgressCreate(context.Context, *WorkDoneProgressCreateParams) error // window/workDoneProgress/create + ApplyEdit(context.Context, *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResult, error) // workspace/applyEdit + CodeLensRefresh(context.Context) error // workspace/codeLens/refresh + Configuration(context.Context, *ParamConfiguration) ([]LSPAny, error) // workspace/configuration + WorkspaceFolders(context.Context) ([]WorkspaceFolder, error) // workspace/workspaceFolders +} + +func clientDispatch(ctx context.Context, client Client, reply jsonrpc2.Replier, r jsonrpc2.Request) (bool, error) { + switch r.Method() { + case "$/logTrace": + var params LogTraceParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.LogTrace(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "$/progress": + var params ProgressParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.Progress(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "client/registerCapability": + var params RegistrationParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.RegisterCapability(ctx, ¶ms) + return true, reply(ctx, nil, err) // 155 + case "client/unregisterCapability": + var params UnregistrationParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.UnregisterCapability(ctx, ¶ms) + return true, reply(ctx, nil, err) // 155 + case "telemetry/event": + var params interface{} + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.Event(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "textDocument/publishDiagnostics": + var params PublishDiagnosticsParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.PublishDiagnostics(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "window/logMessage": + var params LogMessageParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.LogMessage(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "window/showDocument": + var params ShowDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := client.ShowDocument(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "window/showMessage": + var params ShowMessageParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.ShowMessage(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "window/showMessageRequest": + var params ShowMessageRequestParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := client.ShowMessageRequest(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "window/workDoneProgress/create": + var params WorkDoneProgressCreateParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := client.WorkDoneProgressCreate(ctx, ¶ms) + return true, reply(ctx, nil, err) // 155 + case "workspace/applyEdit": + var params ApplyWorkspaceEditParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := client.ApplyEdit(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspace/codeLens/refresh": + err := client.CodeLensRefresh(ctx) + return true, reply(ctx, nil, err) // 170 + case "workspace/configuration": + var params ParamConfiguration + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := client.Configuration(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspace/workspaceFolders": + resp, err := client.WorkspaceFolders(ctx) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 165 + default: + return false, nil + } +} + +func (s *clientDispatcher) LogTrace(ctx context.Context, params *LogTraceParams) error { + return s.sender.Notify(ctx, "$/logTrace", params) +} // 244 +func (s *clientDispatcher) Progress(ctx context.Context, params *ProgressParams) error { + return s.sender.Notify(ctx, "$/progress", params) +} // 244 +func (s *clientDispatcher) RegisterCapability(ctx context.Context, params *RegistrationParams) error { + return s.sender.Call(ctx, "client/registerCapability", params, nil) +} // 194 +func (s *clientDispatcher) UnregisterCapability(ctx context.Context, params *UnregistrationParams) error { + return s.sender.Call(ctx, "client/unregisterCapability", params, nil) +} // 194 +func (s *clientDispatcher) Event(ctx context.Context, params *interface{}) error { + return s.sender.Notify(ctx, "telemetry/event", params) +} // 244 +func (s *clientDispatcher) PublishDiagnostics(ctx context.Context, params *PublishDiagnosticsParams) error { + return s.sender.Notify(ctx, "textDocument/publishDiagnostics", params) +} // 244 +func (s *clientDispatcher) LogMessage(ctx context.Context, params *LogMessageParams) error { + return s.sender.Notify(ctx, "window/logMessage", params) +} // 244 +func (s *clientDispatcher) ShowDocument(ctx context.Context, params *ShowDocumentParams) (*ShowDocumentResult, error) { + var result *ShowDocumentResult + if err := s.sender.Call(ctx, "window/showDocument", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *clientDispatcher) ShowMessage(ctx context.Context, params *ShowMessageParams) error { + return s.sender.Notify(ctx, "window/showMessage", params) +} // 244 +func (s *clientDispatcher) ShowMessageRequest(ctx context.Context, params *ShowMessageRequestParams) (*MessageActionItem, error) { + var result *MessageActionItem + if err := s.sender.Call(ctx, "window/showMessageRequest", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *clientDispatcher) WorkDoneProgressCreate(ctx context.Context, params *WorkDoneProgressCreateParams) error { + return s.sender.Call(ctx, "window/workDoneProgress/create", params, nil) +} // 194 +func (s *clientDispatcher) ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResult, error) { + var result *ApplyWorkspaceEditResult + if err := s.sender.Call(ctx, "workspace/applyEdit", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *clientDispatcher) CodeLensRefresh(ctx context.Context) error { + return s.sender.Call(ctx, "workspace/codeLens/refresh", nil, nil) +} // 209 +func (s *clientDispatcher) Configuration(ctx context.Context, params *ParamConfiguration) ([]LSPAny, error) { + var result []LSPAny + if err := s.sender.Call(ctx, "workspace/configuration", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *clientDispatcher) WorkspaceFolders(ctx context.Context) ([]WorkspaceFolder, error) { + var result []WorkspaceFolder + if err := s.sender.Call(ctx, "workspace/workspaceFolders", nil, &result); err != nil { + return nil, err + } + return result, nil +} // 204 diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/tsdocument_changes.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsdocument_changes.go new file mode 100644 index 0000000..7296a15 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsdocument_changes.go @@ -0,0 +1,41 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package protocol + +import ( + "encoding/json" + "fmt" +) + +// DocumentChanges is a union of a file edit and directory rename operations +// for package renaming feature. At most one field of this struct is non-nil. +type DocumentChanges struct { + TextDocumentEdit *TextDocumentEdit + RenameFile *RenameFile +} + +func (d *DocumentChanges) UnmarshalJSON(data []byte) error { + var m map[string]interface{} + + if err := json.Unmarshal(data, &m); err != nil { + return err + } + + if _, ok := m["textDocument"]; ok { + d.TextDocumentEdit = new(TextDocumentEdit) + return json.Unmarshal(data, d.TextDocumentEdit) + } + + d.RenameFile = new(RenameFile) + return json.Unmarshal(data, d.RenameFile) +} + +func (d *DocumentChanges) MarshalJSON() ([]byte, error) { + if d.TextDocumentEdit != nil { + return json.Marshal(d.TextDocumentEdit) + } else if d.RenameFile != nil { + return json.Marshal(d.RenameFile) + } + return nil, fmt.Errorf("Empty DocumentChanges union value") +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/tsjson.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsjson.go new file mode 100644 index 0000000..3c9781a --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsjson.go @@ -0,0 +1,440 @@ +// Copyright 2019-2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +// Code generated from version 3.17.0 of protocol/metaModel.json. +// git hash 8de18faed635819dd2bc631d2c26ce4a18f7cf4a (as of Fri Sep 16 13:04:31 2022) +// Code generated; DO NOT EDIT. + +import "encoding/json" +import "errors" +import "fmt" + +func (t OrFEditRangePItemDefaults) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case FEditRangePItemDefaults: + return json.Marshal(x) + case Range: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [FEditRangePItemDefaults Range]", t) +} + +func (t *OrFEditRangePItemDefaults) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 FEditRangePItemDefaults + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 Range + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [FEditRangePItemDefaults Range]") +} + +func (t OrFNotebookPNotebookSelector) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case NotebookDocumentFilter: + return json.Marshal(x) + case string: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [NotebookDocumentFilter string]", t) +} + +func (t *OrFNotebookPNotebookSelector) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 NotebookDocumentFilter + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 string + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [NotebookDocumentFilter string]") +} + +func (t OrPLocation_workspace_symbol) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case Location: + return json.Marshal(x) + case PLocationMsg_workspace_symbol: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [Location PLocationMsg_workspace_symbol]", t) +} + +func (t *OrPLocation_workspace_symbol) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 Location + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 PLocationMsg_workspace_symbol + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [Location PLocationMsg_workspace_symbol]") +} + +func (t OrPSection_workspace_didChangeConfiguration) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case []string: + return json.Marshal(x) + case string: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [[]string string]", t) +} + +func (t *OrPSection_workspace_didChangeConfiguration) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 []string + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 string + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [[]string string]") +} + +func (t OrPTooltipPLabel) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case MarkupContent: + return json.Marshal(x) + case string: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [MarkupContent string]", t) +} + +func (t *OrPTooltipPLabel) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 MarkupContent + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 string + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [MarkupContent string]") +} + +func (t OrPTooltip_textDocument_inlayHint) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case MarkupContent: + return json.Marshal(x) + case string: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [MarkupContent string]", t) +} + +func (t *OrPTooltip_textDocument_inlayHint) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 MarkupContent + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 string + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [MarkupContent string]") +} + +func (t Or_Definition) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case Location: + return json.Marshal(x) + case []Location: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [Location []Location]", t) +} + +func (t *Or_Definition) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 Location + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 []Location + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [Location []Location]") +} + +func (t Or_DocumentDiagnosticReport) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case RelatedFullDocumentDiagnosticReport: + return json.Marshal(x) + case RelatedUnchangedDocumentDiagnosticReport: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [RelatedFullDocumentDiagnosticReport RelatedUnchangedDocumentDiagnosticReport]", t) +} + +func (t *Or_DocumentDiagnosticReport) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 RelatedFullDocumentDiagnosticReport + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 RelatedUnchangedDocumentDiagnosticReport + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [RelatedFullDocumentDiagnosticReport RelatedUnchangedDocumentDiagnosticReport]") +} + +func (t Or_DocumentFilter) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case NotebookCellTextDocumentFilter: + return json.Marshal(x) + case TextDocumentFilter: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [NotebookCellTextDocumentFilter TextDocumentFilter]", t) +} + +func (t *Or_DocumentFilter) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 NotebookCellTextDocumentFilter + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 TextDocumentFilter + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [NotebookCellTextDocumentFilter TextDocumentFilter]") +} + +func (t Or_InlineValue) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case InlineValueEvaluatableExpression: + return json.Marshal(x) + case InlineValueText: + return json.Marshal(x) + case InlineValueVariableLookup: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [InlineValueEvaluatableExpression InlineValueText InlineValueVariableLookup]", t) +} + +func (t *Or_InlineValue) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 InlineValueEvaluatableExpression + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 InlineValueText + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + var h2 InlineValueVariableLookup + if err := json.Unmarshal(x, &h2); err == nil { + t.Value = h2 + return nil + } + return errors.New("unmarshal failed to match one of [InlineValueEvaluatableExpression InlineValueText InlineValueVariableLookup]") +} + +func (t Or_MarkedString) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case Msg_MarkedString: + return json.Marshal(x) + case string: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [Msg_MarkedString string]", t) +} + +func (t *Or_MarkedString) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 Msg_MarkedString + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 string + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [Msg_MarkedString string]") +} + +func (t Or_RelativePattern_baseUri) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case URI: + return json.Marshal(x) + case WorkspaceFolder: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [URI WorkspaceFolder]", t) +} + +func (t *Or_RelativePattern_baseUri) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 URI + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 WorkspaceFolder + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [URI WorkspaceFolder]") +} + +func (t Or_WorkspaceDocumentDiagnosticReport) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case WorkspaceFullDocumentDiagnosticReport: + return json.Marshal(x) + case WorkspaceUnchangedDocumentDiagnosticReport: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [WorkspaceFullDocumentDiagnosticReport WorkspaceUnchangedDocumentDiagnosticReport]", t) +} + +func (t *Or_WorkspaceDocumentDiagnosticReport) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 WorkspaceFullDocumentDiagnosticReport + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 WorkspaceUnchangedDocumentDiagnosticReport + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [WorkspaceFullDocumentDiagnosticReport WorkspaceUnchangedDocumentDiagnosticReport]") +} + +func (t Or_textDocument_declaration) MarshalJSON() ([]byte, error) { + switch x := t.Value.(type) { + case Declaration: + return json.Marshal(x) + case []DeclarationLink: + return json.Marshal(x) + case nil: + return []byte("null"), nil + } + return nil, fmt.Errorf("type %T not one of [Declaration []DeclarationLink]", t) +} + +func (t *Or_textDocument_declaration) UnmarshalJSON(x []byte) error { + if string(x) == "null" { + t.Value = nil + return nil + } + var h0 Declaration + if err := json.Unmarshal(x, &h0); err == nil { + t.Value = h0 + return nil + } + var h1 []DeclarationLink + if err := json.Unmarshal(x, &h1); err == nil { + t.Value = h1 + return nil + } + return errors.New("unmarshal failed to match one of [Declaration []DeclarationLink]") +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/tsprotocol.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsprotocol.go new file mode 100644 index 0000000..8272d7e --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsprotocol.go @@ -0,0 +1,6159 @@ +// Copyright 2019-2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +// Code generated from version 3.17.0 of protocol/metaModel.json. +// git hash 8de18faed635819dd2bc631d2c26ce4a18f7cf4a (as of Fri Sep 16 13:04:31 2022) +// Code generated; DO NOT EDIT. + +import "encoding/json" + +/* + * A special text edit with an additional change annotation. + * + * @since 3.16.0. + */ +type AnnotatedTextEdit struct { // line 9392 + // The actual identifier of the change annotation + AnnotationID ChangeAnnotationIdentifier `json:"annotationId"` + TextEdit +} + +// The parameters passed via a apply workspace edit request. +type ApplyWorkspaceEditParams struct { // line 6003 + /* + * An optional label of the workspace edit. This label is + * presented in the user interface for example on an undo + * stack to undo the workspace edit. + */ + Label string `json:"label,omitempty"` + // The edits to apply. + Edit WorkspaceEdit `json:"edit"` +} + +/* + * The result returned from the apply workspace edit request. + * + * @since 3.17 renamed from ApplyWorkspaceEditResponse + */ +type ApplyWorkspaceEditResult struct { // line 6026 + // Indicates whether the edit was applied or not. + Applied bool `json:"applied"` + /* + * An optional textual description for why the edit was not applied. + * This may be used by the server for diagnostic logging or to provide + * a suitable error for a request that triggered the edit. + */ + FailureReason string `json:"failureReason,omitempty"` + /* + * Depending on the client's failure handling strategy `failedChange` might + * contain the index of the change that failed. This property is only available + * if the client signals a `failureHandlingStrategy` in its client capabilities. + */ + FailedChange uint32 `json:"failedChange,omitempty"` +} + +// A base for all symbol information. +type BaseSymbolInformation struct { // line 8986 + // The name of this symbol. + Name string `json:"name"` + // The kind of this symbol. + Kind SymbolKind `json:"kind"` + /* + * Tags for this symbol. + * + * @since 3.16.0 + */ + Tags []SymbolTag `json:"tags,omitempty"` + /* + * The name of the symbol containing this symbol. This information is for + * user interface purposes (e.g. to render a qualifier in the user interface + * if necessary). It can't be used to re-infer a hierarchy for the document + * symbols. + */ + ContainerName string `json:"containerName,omitempty"` +} + +// @since 3.16.0 +type CallHierarchyClientCapabilities struct { // line 12167 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/* + * Represents an incoming call, e.g. a caller of a method or constructor. + * + * @since 3.16.0 + */ +type CallHierarchyIncomingCall struct { // line 2801 + // The item that makes the call. + From CallHierarchyItem `json:"from"` + /* + * The ranges at which the calls appear. This is relative to the caller + * denoted by [`this.from`](#CallHierarchyIncomingCall.from). + */ + FromRanges []Range `json:"fromRanges"` +} + +/* + * The parameter of a `callHierarchy/incomingCalls` request. + * + * @since 3.16.0 + */ +type CallHierarchyIncomingCallsParams struct { // line 2777 + Item CallHierarchyItem `json:"item"` + WorkDoneProgressParams + PartialResultParams +} + +/* + * Represents programming constructs like functions or constructors in the context + * of call hierarchy. + * + * @since 3.16.0 + */ +type CallHierarchyItem struct { // line 2678 + // The name of this item. + Name string `json:"name"` + // The kind of this item. + Kind SymbolKind `json:"kind"` + // Tags for this item. + Tags []SymbolTag `json:"tags,omitempty"` + // More detail for this item, e.g. the signature of a function. + Detail string `json:"detail,omitempty"` + // The resource identifier of this item. + URI DocumentURI `json:"uri"` + // The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. + Range Range `json:"range"` + /* + * The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function. + * Must be contained by the [`range`](#CallHierarchyItem.range). + */ + SelectionRange Range `json:"selectionRange"` + /* + * A data entry field that is preserved between a call hierarchy prepare and + * incoming calls or outgoing calls requests. + */ + Data interface{} `json:"data,omitempty"` +} + +/* + * Call hierarchy options used during static registration. + * + * @since 3.16.0 + */ +type CallHierarchyOptions struct { // line 6539 + WorkDoneProgressOptions +} + +/* + * Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc. + * + * @since 3.16.0 + */ +type CallHierarchyOutgoingCall struct { // line 2851 + // The item that is called. + To CallHierarchyItem `json:"to"` + /* + * The range at which this item is called. This is the range relative to the caller, e.g the item + * passed to [`provideCallHierarchyOutgoingCalls`](#CallHierarchyItemProvider.provideCallHierarchyOutgoingCalls) + * and not [`this.to`](#CallHierarchyOutgoingCall.to). + */ + FromRanges []Range `json:"fromRanges"` +} + +/* + * The parameter of a `callHierarchy/outgoingCalls` request. + * + * @since 3.16.0 + */ +type CallHierarchyOutgoingCallsParams struct { // line 2827 + Item CallHierarchyItem `json:"item"` + WorkDoneProgressParams + PartialResultParams +} + +/* + * The parameter of a `textDocument/prepareCallHierarchy` request. + * + * @since 3.16.0 + */ +type CallHierarchyPrepareParams struct { // line 2660 + TextDocumentPositionParams + WorkDoneProgressParams +} + +/* + * Call hierarchy options used during static or dynamic registration. + * + * @since 3.16.0 + */ +type CallHierarchyRegistrationOptions struct { // line 2755 + TextDocumentRegistrationOptions + CallHierarchyOptions + StaticRegistrationOptions +} +type CancelParams struct { // line 6198 + // The request id to cancel. + ID interface{} `json:"id"` +} + +/* + * Additional information that describes document changes. + * + * @since 3.16.0 + */ +type ChangeAnnotation struct { // line 6836 + /* + * A human-readable string describing the actual change. The string + * is rendered prominent in the user interface. + */ + Label string `json:"label"` + /* + * A flag which indicates that user confirmation is needed + * before applying the change. + */ + NeedsConfirmation bool `json:"needsConfirmation,omitempty"` + /* + * A human-readable string which is rendered less prominent in + * the user interface. + */ + Description string `json:"description,omitempty"` +} + +// An identifier to refer to a change annotation stored with a workspace edit. +type ChangeAnnotationIdentifier = string // (alias) line 14002 +// Defines the capabilities provided by the client. +type ClientCapabilities struct { // line 9700 + // Workspace specific client capabilities. + Workspace WorkspaceClientCapabilities `json:"workspace,omitempty"` + // Text document specific client capabilities. + TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"` + /* + * Capabilities specific to the notebook document support. + * + * @since 3.17.0 + */ + NotebookDocument *NotebookDocumentClientCapabilities `json:"notebookDocument,omitempty"` + // Window specific client capabilities. + Window WindowClientCapabilities `json:"window,omitempty"` + /* + * General client capabilities. + * + * @since 3.16.0 + */ + General *GeneralClientCapabilities `json:"general,omitempty"` + // Experimental client capabilities. + Experimental interface{} `json:"experimental,omitempty"` +} + +/* + * A code action represents a change that can be performed in code, e.g. to fix a problem or + * to refactor code. + * + * A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed. + */ +type CodeAction struct { // line 5401 + // A short, human-readable, title for this code action. + Title string `json:"title"` + /* + * The kind of the code action. + * + * Used to filter code actions. + */ + Kind CodeActionKind `json:"kind,omitempty"` + // The diagnostics that this code action resolves. + Diagnostics []Diagnostic `json:"diagnostics,omitempty"` + /* + * Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted + * by keybindings. + * + * A quick fix should be marked preferred if it properly addresses the underlying error. + * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. + * + * @since 3.15.0 + */ + IsPreferred bool `json:"isPreferred,omitempty"` + /* + * Marks that the code action cannot currently be applied. + * + * Clients should follow the following guidelines regarding disabled code actions: + * + * - Disabled code actions are not shown in automatic [lightbulbs](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) + * code action menus. + * + * - Disabled actions are shown as faded out in the code action menu when the user requests a more specific type + * of code action, such as refactorings. + * + * - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions) + * that auto applies a code action and only disabled code actions are returned, the client should show the user an + * error message with `reason` in the editor. + * + * @since 3.16.0 + */ + Disabled *PDisabledMsg_textDocument_codeAction `json:"disabled,omitempty"` + // The workspace edit this code action performs. + Edit WorkspaceEdit `json:"edit,omitempty"` + /* + * A command this code action executes. If a code action + * provides an edit and a command, first the edit is + * executed and then the command. + */ + Command *Command `json:"command,omitempty"` + /* + * A data entry field that is preserved on a code action between + * a `textDocument/codeAction` and a `codeAction/resolve` request. + * + * @since 3.16.0 + */ + Data interface{} `json:"data,omitempty"` +} + +// The Client Capabilities of a [CodeActionRequest](#CodeActionRequest). +type CodeActionClientCapabilities struct { // line 11747 + // Whether code action supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * The client support code action literals of type `CodeAction` as a valid + * response of the `textDocument/codeAction` request. If the property is not + * set the request can only return `Command` literals. + * + * @since 3.8.0 + */ + CodeActionLiteralSupport PCodeActionLiteralSupportPCodeAction `json:"codeActionLiteralSupport,omitempty"` + /* + * Whether code action supports the `isPreferred` property. + * + * @since 3.15.0 + */ + IsPreferredSupport bool `json:"isPreferredSupport,omitempty"` + /* + * Whether code action supports the `disabled` property. + * + * @since 3.16.0 + */ + DisabledSupport bool `json:"disabledSupport,omitempty"` + /* + * Whether code action supports the `data` property which is + * preserved between a `textDocument/codeAction` and a + * `codeAction/resolve` request. + * + * @since 3.16.0 + */ + DataSupport bool `json:"dataSupport,omitempty"` + /* + * Whether the client supports resolving additional code action + * properties via a separate `codeAction/resolve` request. + * + * @since 3.16.0 + */ + ResolveSupport *PResolveSupportPCodeAction `json:"resolveSupport,omitempty"` + /* + * Whether the client honors the change annotations in + * text edits and resource operations returned via the + * `CodeAction#edit` property by for example presenting + * the workspace edit in the user interface and asking + * for confirmation. + * + * @since 3.16.0 + */ + HonorsChangeAnnotations bool `json:"honorsChangeAnnotations,omitempty"` +} + +/* + * Contains additional diagnostic information about the context in which + * a [code action](#CodeActionProvider.provideCodeActions) is run. + */ +type CodeActionContext struct { // line 9052 + /* + * An array of diagnostics known on the client side overlapping the range provided to the + * `textDocument/codeAction` request. They are provided so that the server knows which + * errors are currently presented to the user for the given range. There is no guarantee + * that these accurately reflect the error state of the resource. The primary parameter + * to compute code actions is the provided range. + */ + Diagnostics []Diagnostic `json:"diagnostics"` + /* + * Requested kind of actions to return. + * + * Actions not of this kind are filtered out by the client before being shown. So servers + * can omit computing them. + */ + Only []CodeActionKind `json:"only,omitempty"` + /* + * The reason why code actions were requested. + * + * @since 3.17.0 + */ + TriggerKind CodeActionTriggerKind `json:"triggerKind,omitempty"` +} +type CodeActionKind string // line 13352 +// Provider options for a [CodeActionRequest](#CodeActionRequest). +type CodeActionOptions struct { // line 9091 + /* + * CodeActionKinds that this server may return. + * + * The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server + * may list out every specific kind they provide. + */ + CodeActionKinds []CodeActionKind `json:"codeActionKinds,omitempty"` + /* + * The server provides support to resolve additional + * information for a code action. + * + * @since 3.16.0 + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +// The parameters of a [CodeActionRequest](#CodeActionRequest). +type CodeActionParams struct { // line 5327 + // The document in which the command was invoked. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The range for which the command was invoked. + Range Range `json:"range"` + // Context carrying additional information. + Context CodeActionContext `json:"context"` + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [CodeActionRequest](#CodeActionRequest). +type CodeActionRegistrationOptions struct { // line 5495 + TextDocumentRegistrationOptions + CodeActionOptions +} +type CodeActionTriggerKind uint32 // line 13632 +/* + * Structure to capture a description for an error code. + * + * @since 3.16.0 + */ +type CodeDescription struct { // line 10052 + // An URI to open with more information about the diagnostic error. + Href URI `json:"href"` +} + +/* + * A code lens represents a [command](#Command) that should be shown along with + * source text, like the number of references, a way to run tests, etc. + * + * A code lens is _unresolved_ when no command is associated to it. For performance + * reasons the creation of a code lens and resolving should be done in two stages. + */ +type CodeLens struct { // line 5618 + // The range in which this code lens is valid. Should only span a single line. + Range Range `json:"range"` + // The command this code lens represents. + Command Command `json:"command,omitempty"` + /* + * A data entry field that is preserved on a code lens item between + * a [CodeLensRequest](#CodeLensRequest) and a [CodeLensResolveRequest] + * (#CodeLensResolveRequest) + */ + Data interface{} `json:"data,omitempty"` +} + +// The client capabilities of a [CodeLensRequest](#CodeLensRequest). +type CodeLensClientCapabilities struct { // line 11861 + // Whether code lens supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// Code Lens provider options of a [CodeLensRequest](#CodeLensRequest). +type CodeLensOptions struct { // line 9147 + // Code lens has a resolve provider as well. + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +// The parameters of a [CodeLensRequest](#CodeLensRequest). +type CodeLensParams struct { // line 5594 + // The document to request code lens for. + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [CodeLensRequest](#CodeLensRequest). +type CodeLensRegistrationOptions struct { // line 5650 + TextDocumentRegistrationOptions + CodeLensOptions +} + +// @since 3.16.0 +type CodeLensWorkspaceClientCapabilities struct { // line 11019 + /* + * Whether the client implementation supports a refresh request sent from the + * server to the client. + * + * Note that this event is global and will force the client to refresh all + * code lenses currently shown. It should be used with absolute care and is + * useful for situation where a server for example detect a project wide + * change that requires such a calculation. + */ + RefreshSupport bool `json:"refreshSupport,omitempty"` +} + +// Represents a color in RGBA space. +type Color struct { // line 6438 + // The red component of this color in the range [0-1]. + Red float64 `json:"red"` + // The green component of this color in the range [0-1]. + Green float64 `json:"green"` + // The blue component of this color in the range [0-1]. + Blue float64 `json:"blue"` + // The alpha component of this color in the range [0-1]. + Alpha float64 `json:"alpha"` +} + +// Represents a color range from a document. +type ColorInformation struct { // line 2261 + // The range in the document where this color appears. + Range Range `json:"range"` + // The actual color value for this color range. + Color Color `json:"color"` +} +type ColorPresentation struct { // line 2343 + /* + * The label of this color presentation. It will be shown on the color + * picker header. By default this is also the text that is inserted when selecting + * this color presentation. + */ + Label string `json:"label"` + /* + * An [edit](#TextEdit) which is applied to a document when selecting + * this presentation for the color. When `falsy` the [label](#ColorPresentation.label) + * is used. + */ + TextEdit *TextEdit `json:"textEdit,omitempty"` + /* + * An optional array of additional [text edits](#TextEdit) that are applied when + * selecting this color presentation. Edits must not overlap with the main [edit](#ColorPresentation.textEdit) nor with themselves. + */ + AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"` +} + +// Parameters for a [ColorPresentationRequest](#ColorPresentationRequest). +type ColorPresentationParams struct { // line 2303 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The color to request presentations for. + Color Color `json:"color"` + // The range where the color would be inserted. Serves as a context. + Range Range `json:"range"` + WorkDoneProgressParams + PartialResultParams +} + +/* + * Represents a reference to a command. Provides a title which + * will be used to represent a command in the UI and, optionally, + * an array of arguments which will be passed to the command handler + * function when invoked. + */ +type Command struct { // line 5367 + // Title of the command, like `save`. + Title string `json:"title"` + // The identifier of the actual command handler. + Command string `json:"command"` + /* + * Arguments that the command handler should be + * invoked with. + */ + Arguments []json.RawMessage `json:"arguments,omitempty"` +} + +// Completion client capabilities +type CompletionClientCapabilities struct { // line 11194 + // Whether completion supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * The client supports the following `CompletionItem` specific + * capabilities. + */ + CompletionItem PCompletionItemPCompletion `json:"completionItem,omitempty"` + CompletionItemKind *PCompletionItemKindPCompletion `json:"completionItemKind,omitempty"` + /* + * Defines how the client handles whitespace and indentation + * when accepting a completion item that uses multi line + * text in either `insertText` or `textEdit`. + * + * @since 3.17.0 + */ + InsertTextMode InsertTextMode `json:"insertTextMode,omitempty"` + /* + * The client supports to send additional context information for a + * `textDocument/completion` request. + */ + ContextSupport bool `json:"contextSupport,omitempty"` + /* + * The client supports the following `CompletionList` specific + * capabilities. + * + * @since 3.17.0 + */ + CompletionList *PCompletionListPCompletion `json:"completionList,omitempty"` +} + +// Contains additional information about the context in which a completion request is triggered. +type CompletionContext struct { // line 8648 + // How the completion was triggered. + TriggerKind CompletionTriggerKind `json:"triggerKind"` + /* + * The trigger character (a single character) that has trigger code complete. + * Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` + */ + TriggerCharacter string `json:"triggerCharacter,omitempty"` +} + +/* + * A completion item represents a text snippet that is + * proposed to complete text that is being typed. + */ +type CompletionItem struct { // line 4550 + /* + * The label of this completion item. + * + * The label property is also by default the text that + * is inserted when selecting this completion. + * + * If label details are provided the label itself should + * be an unqualified name of the completion item. + */ + Label string `json:"label"` + /* + * Additional details for the label + * + * @since 3.17.0 + */ + LabelDetails *CompletionItemLabelDetails `json:"labelDetails,omitempty"` + /* + * The kind of this completion item. Based of the kind + * an icon is chosen by the editor. + */ + Kind CompletionItemKind `json:"kind,omitempty"` + /* + * Tags for this completion item. + * + * @since 3.15.0 + */ + Tags []CompletionItemTag `json:"tags,omitempty"` + /* + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + Detail string `json:"detail,omitempty"` + // A human-readable string that represents a doc-comment. + Documentation string `json:"documentation,omitempty"` + /* + * Indicates if this item is deprecated. + * @deprecated Use `tags` instead. + */ + Deprecated bool `json:"deprecated,omitempty"` + /* + * Select this item when showing. + * + * *Note* that only one completion item can be selected and that the + * tool / client decides which item that is. The rule is that the *first* + * item of those that match best is selected. + */ + Preselect bool `json:"preselect,omitempty"` + /* + * A string that should be used when comparing this item + * with other items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + SortText string `json:"sortText,omitempty"` + /* + * A string that should be used when filtering a set of + * completion items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + FilterText string `json:"filterText,omitempty"` + /* + * A string that should be inserted into a document when selecting + * this completion. When `falsy` the [label](#CompletionItem.label) + * is used. + * + * The `insertText` is subject to interpretation by the client side. + * Some tools might not take the string literally. For example + * VS Code when code complete is requested in this example + * `con` and a completion item with an `insertText` of + * `console` is provided it will only insert `sole`. Therefore it is + * recommended to use `textEdit` instead since it avoids additional client + * side interpretation. + */ + InsertText string `json:"insertText,omitempty"` + /* + * The format of the insert text. The format applies to both the + * `insertText` property and the `newText` property of a provided + * `textEdit`. If omitted defaults to `InsertTextFormat.PlainText`. + * + * Please note that the insertTextFormat doesn't apply to + * `additionalTextEdits`. + */ + InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"` + /* + * How whitespace and indentation is handled during completion + * item insertion. If not provided the clients default value depends on + * the `textDocument.completion.insertTextMode` client capability. + * + * @since 3.16.0 + */ + InsertTextMode InsertTextMode `json:"insertTextMode,omitempty"` + /* + * An [edit](#TextEdit) which is applied to a document when selecting + * this completion. When an edit is provided the value of + * [insertText](#CompletionItem.insertText) is ignored. + * + * Most editors support two different operations when accepting a completion + * item. One is to insert a completion text and the other is to replace an + * existing text with a completion text. Since this can usually not be + * predetermined by a server it can report both ranges. Clients need to + * signal support for `InsertReplaceEdits` via the + * `textDocument.completion.insertReplaceSupport` client capability + * property. + * + * *Note 1:* The text edit's range as well as both ranges from an insert + * replace edit must be a [single line] and they must contain the position + * at which completion has been requested. + * *Note 2:* If an `InsertReplaceEdit` is returned the edit's insert range + * must be a prefix of the edit's replace range, that means it must be + * contained and starting at the same position. + * + * @since 3.16.0 additional type `InsertReplaceEdit` + */ + TextEdit *TextEdit `json:"textEdit,omitempty"` + /* + * The edit text used if the completion item is part of a CompletionList and + * CompletionList defines an item default for the text edit range. + * + * Clients will only honor this property if they opt into completion list + * item defaults using the capability `completionList.itemDefaults`. + * + * If not provided and a list's default range is provided the label + * property is used as a text. + * + * @since 3.17.0 + */ + TextEditText string `json:"textEditText,omitempty"` + /* + * An optional array of additional [text edits](#TextEdit) that are applied when + * selecting this completion. Edits must not overlap (including the same insert position) + * with the main [edit](#CompletionItem.textEdit) nor with themselves. + * + * Additional text edits should be used to change text unrelated to the current cursor position + * (for example adding an import statement at the top of the file if the completion item will + * insert an unqualified type). + */ + AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"` + /* + * An optional set of characters that when pressed while this completion is active will accept it first and + * then type that character. *Note* that all commit characters should have `length=1` and that superfluous + * characters will be ignored. + */ + CommitCharacters []string `json:"commitCharacters,omitempty"` + /* + * An optional [command](#Command) that is executed *after* inserting this completion. *Note* that + * additional modifications to the current document should be described with the + * [additionalTextEdits](#CompletionItem.additionalTextEdits)-property. + */ + Command *Command `json:"command,omitempty"` + /* + * A data entry field that is preserved on a completion item between a + * [CompletionRequest](#CompletionRequest) and a [CompletionResolveRequest](#CompletionResolveRequest). + */ + Data interface{} `json:"data,omitempty"` +} +type CompletionItemKind uint32 // line 13160 +/* + * Additional details for a completion item label. + * + * @since 3.17.0 + */ +type CompletionItemLabelDetails struct { // line 8671 + /* + * An optional string which is rendered less prominently directly after {@link CompletionItem.label label}, + * without any spacing. Should be used for function signatures and type annotations. + */ + Detail string `json:"detail,omitempty"` + /* + * An optional string which is rendered less prominently after {@link CompletionItem.detail}. Should be used + * for fully qualified names and file paths. + */ + Description string `json:"description,omitempty"` +} +type CompletionItemTag uint32 // line 13270 +/* + * Represents a collection of [completion items](#CompletionItem) to be presented + * in the editor. + */ +type CompletionList struct { // line 4758 + /* + * This list it not complete. Further typing results in recomputing this list. + * + * Recomputed lists have all their items replaced (not appended) in the + * incomplete completion sessions. + */ + IsIncomplete bool `json:"isIncomplete"` + /* + * In many cases the items of an actual completion result share the same + * value for properties like `commitCharacters` or the range of a text + * edit. A completion list can therefore define item defaults which will + * be used if a completion item itself doesn't specify the value. + * + * If a completion list specifies a default value and a completion item + * also specifies a corresponding value the one from the item is used. + * + * Servers are only allowed to return default values if the client + * signals support for this via the `completionList.itemDefaults` + * capability. + * + * @since 3.17.0 + */ + ItemDefaults *PItemDefaultsMsg_textDocument_completion `json:"itemDefaults,omitempty"` + // The completion items. + Items []CompletionItem `json:"items"` +} + +// Completion options. +type CompletionOptions struct { // line 8727 + /* + * Most tools trigger completion request automatically without explicitly requesting + * it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user + * starts to type an identifier. For example if the user types `c` in a JavaScript file + * code complete will automatically pop up present `console` besides others as a + * completion item. Characters that make up identifiers don't need to be listed here. + * + * If code complete should automatically be trigger on characters not being valid inside + * an identifier (for example `.` in JavaScript) list them in `triggerCharacters`. + */ + TriggerCharacters []string `json:"triggerCharacters,omitempty"` + /* + * The list of all possible characters that commit a completion. This field can be used + * if clients don't support individual commit characters per completion item. See + * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` + * + * If a server provides both `allCommitCharacters` and commit characters on an individual + * completion item the ones on the completion item win. + * + * @since 3.2.0 + */ + AllCommitCharacters []string `json:"allCommitCharacters,omitempty"` + /* + * The server provides support to resolve additional + * information for a completion item. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + /* + * The server supports the following `CompletionItem` specific + * capabilities. + * + * @since 3.17.0 + */ + CompletionItem *PCompletionItemPCompletionProvider `json:"completionItem,omitempty"` + WorkDoneProgressOptions +} + +// Completion parameters +type CompletionParams struct { // line 4519 + /* + * The completion context. This is only available it the client specifies + * to send this using the client capability `textDocument.completion.contextSupport === true` + */ + Context CompletionContext `json:"context,omitempty"` + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [CompletionRequest](#CompletionRequest). +type CompletionRegistrationOptions struct { // line 4875 + TextDocumentRegistrationOptions + CompletionOptions +} +type CompletionTriggerKind uint32 // line 13581 +type ConfigurationItem struct { // line 6401 + // The scope to get the configuration section for. + ScopeURI string `json:"scopeUri,omitempty"` + // The configuration section asked for. + Section string `json:"section,omitempty"` +} + +// The parameters of a configuration request. +type ConfigurationParams struct { // line 2207 + Items []ConfigurationItem `json:"items"` +} + +// Create file operation. +type CreateFile struct { // line 6717 + // A create + Kind string `json:"kind"` + // The resource to create. + URI DocumentURI `json:"uri"` + // Additional options + Options *CreateFileOptions `json:"options,omitempty"` + ResourceOperation +} + +// Options to create a file. +type CreateFileOptions struct { // line 9437 + // Overwrite existing file. Overwrite wins over `ignoreIfExists` + Overwrite bool `json:"overwrite,omitempty"` + // Ignore if exists. + IgnoreIfExists bool `json:"ignoreIfExists,omitempty"` +} + +/* + * The parameters sent in notifications/requests for user-initiated creation of + * files. + * + * @since 3.16.0 + */ +type CreateFilesParams struct { // line 3197 + // An array of all files/folders created in this operation. + Files []FileCreate `json:"files"` +} + +// The declaration of a symbol representation as one or many [locations](#Location). +type Declaration = []Location // (alias) line 13859 +// @since 3.14.0 +type DeclarationClientCapabilities struct { // line 11535 + /* + * Whether declaration supports dynamic registration. If this is set to `true` + * the client supports the new `DeclarationRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + // The client supports additional metadata in the form of declaration links. + LinkSupport bool `json:"linkSupport,omitempty"` +} + +/* + * Information about where a symbol is declared. + * + * Provides additional metadata over normal [location](#Location) declarations, including the range of + * the declaring symbol. + * + * Servers should prefer returning `DeclarationLink` over `Declaration` if supported + * by the client. + */ +type DeclarationLink = LocationLink // (alias) line 13879 +type DeclarationOptions struct { // line 6496 + WorkDoneProgressOptions +} +type DeclarationParams struct { // line 2516 + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} +type DeclarationRegistrationOptions struct { // line 2536 + DeclarationOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} + +/* + * The definition of a symbol represented as one or many [locations](#Location). + * For most programming languages there is only one location at which a symbol is + * defined. + * + * Servers should prefer returning `DefinitionLink` over `Definition` if supported + * by the client. + */ +type Definition = Or_Definition // (alias) line 13777 +// Client Capabilities for a [DefinitionRequest](#DefinitionRequest). +type DefinitionClientCapabilities struct { // line 11560 + // Whether definition supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * The client supports additional metadata in the form of definition links. + * + * @since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +/* + * Information about where a symbol is defined. + * + * Provides additional metadata over normal [location](#Location) definitions, including the range of + * the defining symbol + */ +type DefinitionLink = LocationLink // (alias) line 13797 +// Server Capabilities for a [DefinitionRequest](#DefinitionRequest). +type DefinitionOptions struct { // line 8939 + WorkDoneProgressOptions +} + +// Parameters for a [DefinitionRequest](#DefinitionRequest). +type DefinitionParams struct { // line 5039 + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [DefinitionRequest](#DefinitionRequest). +type DefinitionRegistrationOptions struct { // line 5060 + TextDocumentRegistrationOptions + DefinitionOptions +} + +// Delete file operation +type DeleteFile struct { // line 6799 + // A delete + Kind string `json:"kind"` + // The file to delete. + URI DocumentURI `json:"uri"` + // Delete options. + Options *DeleteFileOptions `json:"options,omitempty"` + ResourceOperation +} + +// Delete file options +type DeleteFileOptions struct { // line 9485 + // Delete the content recursively if a folder is denoted. + Recursive bool `json:"recursive,omitempty"` + // Ignore the operation if the file doesn't exist. + IgnoreIfNotExists bool `json:"ignoreIfNotExists,omitempty"` +} + +/* + * The parameters sent in notifications/requests for user-initiated deletes of + * files. + * + * @since 3.16.0 + */ +type DeleteFilesParams struct { // line 3322 + // An array of all files/folders deleted in this operation. + Files []FileDelete `json:"files"` +} + +/* + * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects + * are only valid in the scope of a resource. + */ +type Diagnostic struct { // line 8545 + // The range at which the message applies + Range Range `json:"range"` + /* + * The diagnostic's severity. Can be omitted. If omitted it is up to the + * client to interpret diagnostics as error, warning, info or hint. + */ + Severity DiagnosticSeverity `json:"severity,omitempty"` + // The diagnostic's code, which usually appear in the user interface. + Code interface{} `json:"code,omitempty"` + /* + * An optional property to describe the error code. + * Requires the code field (above) to be present/not null. + * + * @since 3.16.0 + */ + CodeDescription *CodeDescription `json:"codeDescription,omitempty"` + /* + * A human-readable string describing the source of this + * diagnostic, e.g. 'typescript' or 'super lint'. It usually + * appears in the user interface. + */ + Source string `json:"source,omitempty"` + // The diagnostic's message. It usually appears in the user interface + Message string `json:"message"` + /* + * Additional metadata about the diagnostic. + * + * @since 3.15.0 + */ + Tags []DiagnosticTag `json:"tags,omitempty"` + /* + * An array of related diagnostic information, e.g. when symbol-names within + * a scope collide all definitions can be marked via this property. + */ + RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"` + /* + * A data entry field that is preserved between a `textDocument/publishDiagnostics` + * notification and `textDocument/codeAction` request. + * + * @since 3.16.0 + */ + Data interface{} `json:"data,omitempty"` +} + +/* + * Client capabilities specific to diagnostic pull requests. + * + * @since 3.17.0 + */ +type DiagnosticClientCapabilities struct { // line 12434 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + // Whether the clients supports related documents for document diagnostic pulls. + RelatedDocumentSupport bool `json:"relatedDocumentSupport,omitempty"` +} + +/* + * Diagnostic options. + * + * @since 3.17.0 + */ +type DiagnosticOptions struct { // line 7298 + /* + * An optional identifier under which the diagnostics are + * managed by the client. + */ + Identifier string `json:"identifier,omitempty"` + /* + * Whether the language has inter file dependencies meaning that + * editing code in one file can result in a different diagnostic + * set in another file. Inter file dependencies are common for + * most programming languages and typically uncommon for linters. + */ + InterFileDependencies bool `json:"interFileDependencies"` + // The server provides support for workspace diagnostics as well. + WorkspaceDiagnostics bool `json:"workspaceDiagnostics"` + WorkDoneProgressOptions +} + +/* + * Diagnostic registration options. + * + * @since 3.17.0 + */ +type DiagnosticRegistrationOptions struct { // line 3877 + TextDocumentRegistrationOptions + DiagnosticOptions + StaticRegistrationOptions +} + +/* + * Represents a related message and source code location for a diagnostic. This should be + * used to point to code locations that cause or related to a diagnostics, e.g when duplicating + * a symbol in a scope. + */ +type DiagnosticRelatedInformation struct { // line 10067 + // The location of this related diagnostic information. + Location Location `json:"location"` + // The message of this related diagnostic information. + Message string `json:"message"` +} + +/* + * Cancellation data returned from a diagnostic request. + * + * @since 3.17.0 + */ +type DiagnosticServerCancellationData struct { // line 3863 + RetriggerRequest bool `json:"retriggerRequest"` +} +type DiagnosticSeverity uint32 // line 13530 +type DiagnosticTag uint32 // line 13560 +/* + * Workspace client capabilities specific to diagnostic pull requests. + * + * @since 3.17.0 + */ +type DiagnosticWorkspaceClientCapabilities struct { // line 11137 + /* + * Whether the client implementation supports a refresh request sent from + * the server to the client. + * + * Note that this event is global and will force the client to refresh all + * pulled diagnostics currently shown. It should be used with absolute care and + * is useful for situation where a server for example detects a project wide + * change that requires such a calculation. + */ + RefreshSupport bool `json:"refreshSupport,omitempty"` +} +type DidChangeConfigurationClientCapabilities struct { // line 10863 + // Did change configuration notification supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// The parameters of a change configuration notification. +type DidChangeConfigurationParams struct { // line 4166 + // The actual changed settings + Settings interface{} `json:"settings"` +} +type DidChangeConfigurationRegistrationOptions struct { // line 4180 + Section *OrPSection_workspace_didChangeConfiguration `json:"section,omitempty"` +} + +/* + * The params sent in a change notebook document notification. + * + * @since 3.17.0 + */ +type DidChangeNotebookDocumentParams struct { // line 3996 + /* + * The notebook document that did change. The version number points + * to the version after all provided changes have been applied. If + * only the text document content of a cell changes the notebook version + * doesn't necessarily have to change. + */ + NotebookDocument VersionedNotebookDocumentIdentifier `json:"notebookDocument"` + /* + * The actual changes to the notebook document. + * + * The changes describe single state changes to the notebook document. + * So if there are two changes c1 (at array index 0) and c2 (at array + * index 1) for a notebook in state S then c1 moves the notebook from + * S to S' and c2 from S' to S''. So c1 is computed on the state S and + * c2 is computed on the state S'. + * + * To mirror the content of a notebook using change events use the following approach: + * - start with the same initial content + * - apply the 'notebookDocument/didChange' notifications in the order you receive them. + * - apply the `NotebookChangeEvent`s in a single notification in the order + * you receive them. + */ + Change NotebookDocumentChangeEvent `json:"change"` +} + +// The change text document notification's parameters. +type DidChangeTextDocumentParams struct { // line 4309 + /* + * The document that did change. The version number points + * to the version after all provided content changes have + * been applied. + */ + TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` + /* + * The actual content changes. The content changes describe single state changes + * to the document. So if there are two content changes c1 (at array index 0) and + * c2 (at array index 1) for a document in state S then c1 moves the document from + * S to S' and c2 from S' to S''. So c1 is computed on the state S and c2 is computed + * on the state S'. + * + * To mirror the content of a document using change events use the following approach: + * - start with the same initial content + * - apply the 'textDocument/didChange' notifications in the order you receive them. + * - apply the `TextDocumentContentChangeEvent`s in a single notification in the order + * you receive them. + */ + ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` +} +type DidChangeWatchedFilesClientCapabilities struct { // line 10877 + /* + * Did change watched files notification supports dynamic registration. Please note + * that the current protocol doesn't support static configuration for file changes + * from the server side. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * Whether the client has support for {@link RelativePattern relative pattern} + * or not. + * + * @since 3.17.0 + */ + RelativePatternSupport bool `json:"relativePatternSupport,omitempty"` +} + +// The watched files change notification's parameters. +type DidChangeWatchedFilesParams struct { // line 4450 + // The actual file events. + Changes []FileEvent `json:"changes"` +} + +// Describe options to be used when registered for text document change events. +type DidChangeWatchedFilesRegistrationOptions struct { // line 4467 + // The watchers to register. + Watchers []FileSystemWatcher `json:"watchers"` +} + +// The parameters of a `workspace/didChangeWorkspaceFolders` notification. +type DidChangeWorkspaceFoldersParams struct { // line 2193 + // The actual workspace folder change event. + Event WorkspaceFoldersChangeEvent `json:"event"` +} + +/* + * The params sent in a close notebook document notification. + * + * @since 3.17.0 + */ +type DidCloseNotebookDocumentParams struct { // line 4034 + // The notebook document that got closed. + NotebookDocument NotebookDocumentIdentifier `json:"notebookDocument"` + /* + * The text documents that represent the content + * of a notebook cell that got closed. + */ + CellTextDocuments []TextDocumentIdentifier `json:"cellTextDocuments"` +} + +// The parameters sent in a close text document notification +type DidCloseTextDocumentParams struct { // line 4354 + // The document that was closed. + TextDocument TextDocumentIdentifier `json:"textDocument"` +} + +/* + * The params sent in an open notebook document notification. + * + * @since 3.17.0 + */ +type DidOpenNotebookDocumentParams struct { // line 3970 + // The notebook document that got opened. + NotebookDocument NotebookDocument `json:"notebookDocument"` + /* + * The text documents that represent the content + * of a notebook cell. + */ + CellTextDocuments []TextDocumentItem `json:"cellTextDocuments"` +} + +// The parameters sent in an open text document notification +type DidOpenTextDocumentParams struct { // line 4295 + // The document that was opened. + TextDocument TextDocumentItem `json:"textDocument"` +} + +/* + * The params sent in a save notebook document notification. + * + * @since 3.17.0 + */ +type DidSaveNotebookDocumentParams struct { // line 4019 + // The notebook document that got saved. + NotebookDocument NotebookDocumentIdentifier `json:"notebookDocument"` +} + +// The parameters sent in a save text document notification +type DidSaveTextDocumentParams struct { // line 4368 + // The document that was saved. + TextDocument TextDocumentIdentifier `json:"textDocument"` + /* + * Optional the content when saved. Depends on the includeText value + * when the save notification was requested. + */ + Text *string `json:"text,omitempty"` +} +type DocumentColorClientCapabilities struct { // line 11901 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `DocumentColorRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} +type DocumentColorOptions struct { // line 6476 + WorkDoneProgressOptions +} + +// Parameters for a [DocumentColorRequest](#DocumentColorRequest). +type DocumentColorParams struct { // line 2237 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} +type DocumentColorRegistrationOptions struct { // line 2283 + TextDocumentRegistrationOptions + DocumentColorOptions + StaticRegistrationOptions +} + +/* + * Parameters of the document diagnostic request. + * + * @since 3.17.0 + */ +type DocumentDiagnosticParams struct { // line 3790 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The additional identifier provided during registration. + Identifier string `json:"identifier,omitempty"` + // The result id of a previous response if provided. + PreviousResultID string `json:"previousResultId,omitempty"` + WorkDoneProgressParams + PartialResultParams +} + +/* + * The result of a document diagnostic pull request. A report can + * either be a full report containing all diagnostics for the + * requested document or an unchanged report indicating that nothing + * has changed in terms of diagnostics in comparison to the last + * pull request. + * + * @since 3.17.0 + */ +type DocumentDiagnosticReport = Or_DocumentDiagnosticReport // (alias) line 13909 +type DocumentDiagnosticReportKind string // line 12748 +/* + * A partial result for a document diagnostic report. + * + * @since 3.17.0 + */ +type DocumentDiagnosticReportPartialResult struct { // line 3833 + RelatedDocuments map[DocumentURI]interface{} `json:"relatedDocuments"` +} + +/* + * A document filter describes a top level text document or + * a notebook cell document. + * + * @since 3.17.0 - proposed support for NotebookCellTextDocumentFilter. + */ +type DocumentFilter = Or_DocumentFilter // (alias) line 14118 +// Client capabilities of a [DocumentFormattingRequest](#DocumentFormattingRequest). +type DocumentFormattingClientCapabilities struct { // line 11915 + // Whether formatting supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// Provider options for a [DocumentFormattingRequest](#DocumentFormattingRequest). +type DocumentFormattingOptions struct { // line 9241 + WorkDoneProgressOptions +} + +// The parameters of a [DocumentFormattingRequest](#DocumentFormattingRequest). +type DocumentFormattingParams struct { // line 5746 + // The document to format. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The format options. + Options FormattingOptions `json:"options"` + WorkDoneProgressParams +} + +// Registration options for a [DocumentFormattingRequest](#DocumentFormattingRequest). +type DocumentFormattingRegistrationOptions struct { // line 5774 + TextDocumentRegistrationOptions + DocumentFormattingOptions +} + +/* + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ +type DocumentHighlight struct { // line 5140 + // The range this highlight applies to. + Range Range `json:"range"` + // The highlight kind, default is [text](#DocumentHighlightKind.Text). + Kind DocumentHighlightKind `json:"kind,omitempty"` +} + +// Client Capabilities for a [DocumentHighlightRequest](#DocumentHighlightRequest). +type DocumentHighlightClientCapabilities struct { // line 11650 + // Whether document highlight supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} +type DocumentHighlightKind uint32 // line 13327 +// Provider options for a [DocumentHighlightRequest](#DocumentHighlightRequest). +type DocumentHighlightOptions struct { // line 8975 + WorkDoneProgressOptions +} + +// Parameters for a [DocumentHighlightRequest](#DocumentHighlightRequest). +type DocumentHighlightParams struct { // line 5119 + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [DocumentHighlightRequest](#DocumentHighlightRequest). +type DocumentHighlightRegistrationOptions struct { // line 5163 + TextDocumentRegistrationOptions + DocumentHighlightOptions +} + +/* + * A document link is a range in a text document that links to an internal or external resource, like another + * text document or a web site. + */ +type DocumentLink struct { // line 5689 + // The range this link applies to. + Range Range `json:"range"` + // The uri this link points to. If missing a resolve request is sent later. + Target string `json:"target,omitempty"` + /* + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on how to + * trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, + * user settings, and localization. + * + * @since 3.15.0 + */ + Tooltip string `json:"tooltip,omitempty"` + /* + * A data entry field that is preserved on a document link between a + * DocumentLinkRequest and a DocumentLinkResolveRequest. + */ + Data interface{} `json:"data,omitempty"` +} + +// The client capabilities of a [DocumentLinkRequest](#DocumentLinkRequest). +type DocumentLinkClientCapabilities struct { // line 11876 + // Whether document link supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * Whether the client supports the `tooltip` property on `DocumentLink`. + * + * @since 3.15.0 + */ + TooltipSupport bool `json:"tooltipSupport,omitempty"` +} + +// Provider options for a [DocumentLinkRequest](#DocumentLinkRequest). +type DocumentLinkOptions struct { // line 9168 + // Document links have a resolve provider as well. + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +// The parameters of a [DocumentLinkRequest](#DocumentLinkRequest). +type DocumentLinkParams struct { // line 5665 + // The document to provide document links for. + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [DocumentLinkRequest](#DocumentLinkRequest). +type DocumentLinkRegistrationOptions struct { // line 5731 + TextDocumentRegistrationOptions + DocumentLinkOptions +} + +// Client capabilities of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). +type DocumentOnTypeFormattingClientCapabilities struct { // line 11945 + // Whether on type formatting supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// Provider options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). +type DocumentOnTypeFormattingOptions struct { // line 9263 + // A character on which formatting should be triggered, like `{`. + FirstTriggerCharacter string `json:"firstTriggerCharacter"` + // More trigger characters. + MoreTriggerCharacter []string `json:"moreTriggerCharacter,omitempty"` +} + +// The parameters of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). +type DocumentOnTypeFormattingParams struct { // line 5840 + // The document to format. + TextDocument TextDocumentIdentifier `json:"textDocument"` + /* + * The position around which the on type formatting should happen. + * This is not necessarily the exact position where the character denoted + * by the property `ch` got typed. + */ + Position Position `json:"position"` + /* + * The character that has been typed that triggered the formatting + * on type request. That is not necessarily the last character that + * got inserted into the document since the client could auto insert + * characters as well (e.g. like automatic brace completion). + */ + Ch string `json:"ch"` + // The formatting options. + Options FormattingOptions `json:"options"` +} + +// Registration options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). +type DocumentOnTypeFormattingRegistrationOptions struct { // line 5878 + TextDocumentRegistrationOptions + DocumentOnTypeFormattingOptions +} + +// Client capabilities of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). +type DocumentRangeFormattingClientCapabilities struct { // line 11930 + // Whether range formatting supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// Provider options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). +type DocumentRangeFormattingOptions struct { // line 9252 + WorkDoneProgressOptions +} + +// The parameters of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). +type DocumentRangeFormattingParams struct { // line 5789 + // The document to format. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The range to format + Range Range `json:"range"` + // The format options + Options FormattingOptions `json:"options"` + WorkDoneProgressParams +} + +// Registration options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). +type DocumentRangeFormattingRegistrationOptions struct { // line 5825 + TextDocumentRegistrationOptions + DocumentRangeFormattingOptions +} + +/* + * A document selector is the combination of one or many document filters. + * + * @sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**∕tsconfig.json' }]`; + * + * The use of a string as a document filter is deprecated @since 3.16.0. + */ +type DocumentSelector = []DocumentFilter // (alias) line 13990 +/* + * Represents programming constructs like variables, classes, interfaces etc. + * that appear in a document. Document symbols can be hierarchical and they + * have two ranges: one that encloses its definition and one that points to + * its most interesting range, e.g. the range of an identifier. + */ +type DocumentSymbol struct { // line 5231 + /* + * The name of this symbol. Will be displayed in the user interface and therefore must not be + * an empty string or a string only consisting of white spaces. + */ + Name string `json:"name"` + // More detail for this symbol, e.g the signature of a function. + Detail string `json:"detail,omitempty"` + // The kind of this symbol. + Kind SymbolKind `json:"kind"` + /* + * Tags for this document symbol. + * + * @since 3.16.0 + */ + Tags []SymbolTag `json:"tags,omitempty"` + /* + * Indicates if this symbol is deprecated. + * + * @deprecated Use tags instead + */ + Deprecated bool `json:"deprecated,omitempty"` + /* + * The range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to determine if the clients cursor is + * inside the symbol to reveal in the symbol in the UI. + */ + Range Range `json:"range"` + /* + * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. + * Must be contained by the `range`. + */ + SelectionRange Range `json:"selectionRange"` + // Children of this symbol, e.g. properties of a class. + Children []DocumentSymbol `json:"children,omitempty"` +} + +// Client Capabilities for a [DocumentSymbolRequest](#DocumentSymbolRequest). +type DocumentSymbolClientCapabilities struct { // line 11665 + // Whether document symbol supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * Specific capabilities for the `SymbolKind` in the + * `textDocument/documentSymbol` request. + */ + SymbolKind *PSymbolKindPDocumentSymbol `json:"symbolKind,omitempty"` + // The client supports hierarchical document symbols. + HierarchicalDocumentSymbolSupport bool `json:"hierarchicalDocumentSymbolSupport,omitempty"` + /* + * The client supports tags on `SymbolInformation`. Tags are supported on + * `DocumentSymbol` if `hierarchicalDocumentSymbolSupport` is set to true. + * Clients supporting tags have to handle unknown tags gracefully. + * + * @since 3.16.0 + */ + TagSupport *PTagSupportPDocumentSymbol `json:"tagSupport,omitempty"` + /* + * The client supports an additional label presented in the UI when + * registering a document symbol provider. + * + * @since 3.16.0 + */ + LabelSupport bool `json:"labelSupport,omitempty"` +} + +// Provider options for a [DocumentSymbolRequest](#DocumentSymbolRequest). +type DocumentSymbolOptions struct { // line 9030 + /* + * A human-readable string that is shown when multiple outlines trees + * are shown for the same document. + * + * @since 3.16.0 + */ + Label string `json:"label,omitempty"` + WorkDoneProgressOptions +} + +// Parameters for a [DocumentSymbolRequest](#DocumentSymbolRequest). +type DocumentSymbolParams struct { // line 5178 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [DocumentSymbolRequest](#DocumentSymbolRequest). +type DocumentSymbolRegistrationOptions struct { // line 5312 + TextDocumentRegistrationOptions + DocumentSymbolOptions +} +type DocumentURI string // line 0 +type ErrorCodes int32 // line 12769 +// The client capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest). +type ExecuteCommandClientCapabilities struct { // line 10988 + // Execute command supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// The server capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest). +type ExecuteCommandOptions struct { // line 9311 + // The commands to be executed on the server + Commands []string `json:"commands"` + WorkDoneProgressOptions +} + +// The parameters of a [ExecuteCommandRequest](#ExecuteCommandRequest). +type ExecuteCommandParams struct { // line 5960 + // The identifier of the actual command handler. + Command string `json:"command"` + // Arguments that the command should be invoked with. + Arguments []json.RawMessage `json:"arguments,omitempty"` + WorkDoneProgressParams +} + +// Registration options for a [ExecuteCommandRequest](#ExecuteCommandRequest). +type ExecuteCommandRegistrationOptions struct { // line 5992 + ExecuteCommandOptions +} +type ExecutionSummary struct { // line 10188 + /* + * A strict monotonically increasing value + * indicating the execution order of a cell + * inside a notebook. + */ + ExecutionOrder uint32 `json:"executionOrder"` + /* + * Whether the execution was successful or + * not if known by the client. + */ + Success bool `json:"success,omitempty"` +} + +// created for Literal +type FCellsPNotebookSelector struct { // line 9857 + Language string `json:"language"` +} + +// created for Literal +type FCodeActionKindPCodeActionLiteralSupport struct { // line 11768 + /* + * The code action kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + */ + ValueSet []CodeActionKind `json:"valueSet"` +} + +// created for Literal +type FEditRangePItemDefaults struct { // line 4797 + Insert Range `json:"insert"` + Replace Range `json:"replace"` +} + +// created for Literal +type FFullPRequests struct { // line 12230 + /* + * The client will send the `textDocument/semanticTokens/full/delta` request if + * the server provides a corresponding handler. + */ + Delta bool `json:"delta"` +} + +// created for Literal +type FInsertTextModeSupportPCompletionItem struct { // line 11321 + ValueSet []InsertTextMode `json:"valueSet"` +} + +// created for Literal +type FParameterInformationPSignatureInformation struct { // line 11487 + /* + * The client supports processing label offsets instead of a + * simple label string. + * + * @since 3.14.0 + */ + LabelOffsetSupport bool `json:"labelOffsetSupport"` +} + +// created for Literal +type FRangePRequests struct { // line 12210 +} + +// created for Literal +type FResolveSupportPCompletionItem struct { // line 11297 + // The properties that a client can resolve lazily. + Properties []string `json:"properties"` +} + +// created for Literal +type FStructurePCells struct { // line 7492 + // The change to the cell array. + Array NotebookCellArrayChange `json:"array"` + // Additional opened cell text documents. + DidOpen []TextDocumentItem `json:"didOpen"` + // Additional closed cell text documents. + DidClose []TextDocumentIdentifier `json:"didClose"` +} + +// created for Literal +type FTagSupportPCompletionItem struct { // line 11263 + // The tags supported by the client. + ValueSet []CompletionItemTag `json:"valueSet"` +} + +// created for Literal +type FTextContentPCells struct { // line 7550 + Document VersionedTextDocumentIdentifier `json:"document"` + Changes []TextDocumentContentChangeEvent `json:"changes"` +} +type FailureHandlingKind string // line 13719 +type FileChangeType uint32 // line 13480 +/* + * Represents information on a file/folder create. + * + * @since 3.16.0 + */ +type FileCreate struct { // line 6667 + // A file:// URI for the location of the file/folder being created. + URI string `json:"uri"` +} + +/* + * Represents information on a file/folder delete. + * + * @since 3.16.0 + */ +type FileDelete struct { // line 6916 + // A file:// URI for the location of the file/folder being deleted. + URI string `json:"uri"` +} + +// An event describing a file change. +type FileEvent struct { // line 8500 + // The file's uri. + URI DocumentURI `json:"uri"` + // The change type. + Type FileChangeType `json:"type"` +} + +/* + * Capabilities relating to events from file operations by the user in the client. + * + * These events do not come from the file system, they come from user operations + * like renaming a file in the UI. + * + * @since 3.16.0 + */ +type FileOperationClientCapabilities struct { // line 11035 + // Whether the client supports dynamic registration for file requests/notifications. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + // The client has support for sending didCreateFiles notifications. + DidCreate bool `json:"didCreate,omitempty"` + // The client has support for sending willCreateFiles requests. + WillCreate bool `json:"willCreate,omitempty"` + // The client has support for sending didRenameFiles notifications. + DidRename bool `json:"didRename,omitempty"` + // The client has support for sending willRenameFiles requests. + WillRename bool `json:"willRename,omitempty"` + // The client has support for sending didDeleteFiles notifications. + DidDelete bool `json:"didDelete,omitempty"` + // The client has support for sending willDeleteFiles requests. + WillDelete bool `json:"willDelete,omitempty"` +} + +/* + * A filter to describe in which file operation requests or notifications + * the server is interested in receiving. + * + * @since 3.16.0 + */ +type FileOperationFilter struct { // line 6869 + // A Uri scheme like `file` or `untitled`. + Scheme string `json:"scheme,omitempty"` + // The actual file operation pattern. + Pattern FileOperationPattern `json:"pattern"` +} + +/* + * Options for notifications/requests for user operations on files. + * + * @since 3.16.0 + */ +type FileOperationOptions struct { // line 9991 + // The server is interested in receiving didCreateFiles notifications. + DidCreate *FileOperationRegistrationOptions `json:"didCreate,omitempty"` + // The server is interested in receiving willCreateFiles requests. + WillCreate *FileOperationRegistrationOptions `json:"willCreate,omitempty"` + // The server is interested in receiving didRenameFiles notifications. + DidRename *FileOperationRegistrationOptions `json:"didRename,omitempty"` + // The server is interested in receiving willRenameFiles requests. + WillRename *FileOperationRegistrationOptions `json:"willRename,omitempty"` + // The server is interested in receiving didDeleteFiles file notifications. + DidDelete *FileOperationRegistrationOptions `json:"didDelete,omitempty"` + // The server is interested in receiving willDeleteFiles file requests. + WillDelete *FileOperationRegistrationOptions `json:"willDelete,omitempty"` +} + +/* + * A pattern to describe in which file operation requests or notifications + * the server is interested in receiving. + * + * @since 3.16.0 + */ +type FileOperationPattern struct { // line 9509 + /* + * The glob pattern to match. Glob patterns can have the following syntax: + * - `*` to match one or more characters in a path segment + * - `?` to match on one character in a path segment + * - `**` to match any number of path segments, including none + * - `{}` to group sub patterns into an OR expression. (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + */ + Glob string `json:"glob"` + /* + * Whether to match files or folders with this pattern. + * + * Matches both if undefined. + */ + Matches FileOperationPatternKind `json:"matches,omitempty"` + // Additional options used during matching. + Options *FileOperationPatternOptions `json:"options,omitempty"` +} +type FileOperationPatternKind string // line 13653 +/* + * Matching options for the file operation pattern. + * + * @since 3.16.0 + */ +type FileOperationPatternOptions struct { // line 10172 + // The pattern should be matched ignoring casing. + IgnoreCase bool `json:"ignoreCase,omitempty"` +} + +/* + * The options to register for file operations. + * + * @since 3.16.0 + */ +type FileOperationRegistrationOptions struct { // line 3286 + // The actual filters. + Filters []FileOperationFilter `json:"filters"` +} + +/* + * Represents information on a file/folder rename. + * + * @since 3.16.0 + */ +type FileRename struct { // line 6893 + // A file:// URI for the original location of the file/folder being renamed. + OldURI string `json:"oldUri"` + // A file:// URI for the new location of the file/folder being renamed. + NewURI string `json:"newUri"` +} +type FileSystemWatcher struct { // line 8522 + /* + * The glob pattern to watch. See {@link GlobPattern glob pattern} for more detail. + * + * @since 3.17.0 support for relative patterns. + */ + GlobPattern GlobPattern `json:"globPattern"` + /* + * The kind of events of interest. If omitted it defaults + * to WatchKind.Create | WatchKind.Change | WatchKind.Delete + * which is 7. + */ + Kind WatchKind `json:"kind,omitempty"` +} + +/* + * Represents a folding range. To be valid, start and end line must be bigger than zero and smaller + * than the number of lines in the document. Clients are free to ignore invalid ranges. + */ +type FoldingRange struct { // line 2437 + /* + * The zero-based start line of the range to fold. The folded area starts after the line's last character. + * To be valid, the end must be zero or larger and smaller than the number of lines in the document. + */ + StartLine uint32 `json:"startLine"` + // The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line. + StartCharacter uint32 `json:"startCharacter,omitempty"` + /* + * The zero-based end line of the range to fold. The folded area ends with the line's last character. + * To be valid, the end must be zero or larger and smaller than the number of lines in the document. + */ + EndLine uint32 `json:"endLine"` + // The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line. + EndCharacter uint32 `json:"endCharacter,omitempty"` + /* + * Describes the kind of the folding range such as `comment' or 'region'. The kind + * is used to categorize folding ranges and used by commands like 'Fold all comments'. + * See [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds. + */ + Kind string `json:"kind,omitempty"` + /* + * The text that the client should show when the specified range is + * collapsed. If not defined or not supported by the client, a default + * will be chosen by the client. + * + * @since 3.17.0 + */ + CollapsedText string `json:"collapsedText,omitempty"` +} +type FoldingRangeClientCapabilities struct { // line 12004 + /* + * Whether implementation supports dynamic registration for folding range + * providers. If this is set to `true` the client supports the new + * `FoldingRangeRegistrationOptions` return value for the corresponding + * server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * The maximum number of folding ranges that the client prefers to receive + * per document. The value serves as a hint, servers are free to follow the + * limit. + */ + RangeLimit uint32 `json:"rangeLimit,omitempty"` + /* + * If set, the client signals that it only supports folding complete lines. + * If set, client will ignore specified `startCharacter` and `endCharacter` + * properties in a FoldingRange. + */ + LineFoldingOnly bool `json:"lineFoldingOnly,omitempty"` + /* + * Specific options for the folding range kind. + * + * @since 3.17.0 + */ + FoldingRangeKind *PFoldingRangeKindPFoldingRange `json:"foldingRangeKind,omitempty"` + /* + * Specific options for the folding range. + * + * @since 3.17.0 + */ + FoldingRange *PFoldingRangePFoldingRange `json:"foldingRange,omitempty"` +} +type FoldingRangeKind string // line 12841 +type FoldingRangeOptions struct { // line 6486 + WorkDoneProgressOptions +} + +// Parameters for a [FoldingRangeRequest](#FoldingRangeRequest). +type FoldingRangeParams struct { // line 2413 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} +type FoldingRangeRegistrationOptions struct { // line 2496 + TextDocumentRegistrationOptions + FoldingRangeOptions + StaticRegistrationOptions +} + +// Value-object describing what options formatting should use. +type FormattingOptions struct { // line 9189 + // Size of a tab in spaces. + TabSize uint32 `json:"tabSize"` + // Prefer spaces over tabs. + InsertSpaces bool `json:"insertSpaces"` + /* + * Trim trailing whitespace on a line. + * + * @since 3.15.0 + */ + TrimTrailingWhitespace bool `json:"trimTrailingWhitespace,omitempty"` + /* + * Insert a newline character at the end of the file if one does not exist. + * + * @since 3.15.0 + */ + InsertFinalNewline bool `json:"insertFinalNewline,omitempty"` + /* + * Trim all newlines after the final newline at the end of the file. + * + * @since 3.15.0 + */ + TrimFinalNewlines bool `json:"trimFinalNewlines,omitempty"` +} + +/* + * A diagnostic report with a full set of problems. + * + * @since 3.17.0 + */ +type FullDocumentDiagnosticReport struct { // line 7240 + // A full document diagnostic report. + Kind string `json:"kind"` + /* + * An optional result id. If provided it will + * be sent on the next diagnostic request for the + * same document. + */ + ResultID string `json:"resultId,omitempty"` + // The actual items. + Items []Diagnostic `json:"items"` +} + +/* + * General client capabilities. + * + * @since 3.16.0 + */ +type GeneralClientCapabilities struct { // line 10690 + /* + * Client capability that signals how the client + * handles stale requests (e.g. a request + * for which the client will not process the response + * anymore since the information is outdated). + * + * @since 3.17.0 + */ + StaleRequestSupport *PStaleRequestSupportPGeneral `json:"staleRequestSupport,omitempty"` + /* + * Client capabilities specific to regular expressions. + * + * @since 3.16.0 + */ + RegularExpressions *RegularExpressionsClientCapabilities `json:"regularExpressions,omitempty"` + /* + * Client capabilities specific to the client's markdown parser. + * + * @since 3.16.0 + */ + Markdown *MarkdownClientCapabilities `json:"markdown,omitempty"` + /* + * The position encodings supported by the client. Client and server + * have to agree on the same position encoding to ensure that offsets + * (e.g. character position in a line) are interpreted the same on both + * sides. + * + * To keep the protocol backwards compatible the following applies: if + * the value 'utf-16' is missing from the array of position encodings + * servers can assume that the client supports UTF-16. UTF-16 is + * therefore a mandatory encoding. + * + * If omitted it defaults to ['utf-16']. + * + * Implementation considerations: since the conversion from one encoding + * into another requires the content of the file / line the conversion + * is best done where the file is read which is usually on the server + * side. + * + * @since 3.17.0 + */ + PositionEncodings []PositionEncodingKind `json:"positionEncodings,omitempty"` +} + +/* + * The glob pattern. Either a string pattern or a relative pattern. + * + * @since 3.17.0 + */ +type GlobPattern = string // (alias) line 14136 +// The result of a hover request. +type Hover struct { // line 4907 + // The hover's content + Contents MarkupContent `json:"contents"` + /* + * An optional range inside the text document that is used to + * visualize the hover, e.g. by changing the background color. + */ + Range Range `json:"range,omitempty"` +} +type HoverClientCapabilities struct { // line 11428 + // Whether hover supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * Client supports the following content formats for the content + * property. The order describes the preferred format of the client. + */ + ContentFormat []MarkupKind `json:"contentFormat,omitempty"` +} + +// Hover options. +type HoverOptions struct { // line 8796 + WorkDoneProgressOptions +} + +// Parameters for a [HoverRequest](#HoverRequest). +type HoverParams struct { // line 4890 + TextDocumentPositionParams + WorkDoneProgressParams +} + +// Registration options for a [HoverRequest](#HoverRequest). +type HoverRegistrationOptions struct { // line 4946 + TextDocumentRegistrationOptions + HoverOptions +} + +// @since 3.6.0 +type ImplementationClientCapabilities struct { // line 11609 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `ImplementationRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * The client supports additional metadata in the form of definition links. + * + * @since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} +type ImplementationOptions struct { // line 6338 + WorkDoneProgressOptions +} +type ImplementationParams struct { // line 2071 + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} +type ImplementationRegistrationOptions struct { // line 2111 + TextDocumentRegistrationOptions + ImplementationOptions + StaticRegistrationOptions +} + +/* + * The data type of the ResponseError if the + * initialize request fails. + */ +type InitializeError struct { // line 4148 + /* + * Indicates whether the client execute the following retry logic: + * (1) show the message provided by the ResponseError to the user + * (2) user selects retry or cancel + * (3) if user selected retry the initialize method is sent again. + */ + Retry bool `json:"retry"` +} +type InitializeParams struct { // line 4090 + XInitializeParams + WorkspaceFoldersInitializeParams +} + +// The result returned from an initialize request. +type InitializeResult struct { // line 4104 + // The capabilities the language server provides. + Capabilities ServerCapabilities `json:"capabilities"` + /* + * Information about the server. + * + * @since 3.15.0 + */ + ServerInfo PServerInfoMsg_initialize `json:"serverInfo,omitempty"` +} +type InitializedParams struct { // line 4162 +} + +/* + * Inlay hint information. + * + * @since 3.17.0 + */ +type InlayHint struct { // line 3667 + // The position of this hint. + Position *Position `json:"position"` + /* + * The label of this hint. A human readable string or an array of + * InlayHintLabelPart label parts. + * + * *Note* that neither the string nor the label part can be empty. + */ + Label []InlayHintLabelPart `json:"label"` + /* + * The kind of this hint. Can be omitted in which case the client + * should fall back to a reasonable default. + */ + Kind InlayHintKind `json:"kind,omitempty"` + /* + * Optional text edits that are performed when accepting this inlay hint. + * + * *Note* that edits are expected to change the document so that the inlay + * hint (or its nearest variant) is now part of the document and the inlay + * hint itself is now obsolete. + */ + TextEdits []TextEdit `json:"textEdits,omitempty"` + // The tooltip text when you hover over this item. + Tooltip *OrPTooltip_textDocument_inlayHint `json:"tooltip,omitempty"` + /* + * Render padding before the hint. + * + * Note: Padding should use the editor's background color, not the + * background color of the hint itself. That means padding can be used + * to visually align/separate an inlay hint. + */ + PaddingLeft bool `json:"paddingLeft,omitempty"` + /* + * Render padding after the hint. + * + * Note: Padding should use the editor's background color, not the + * background color of the hint itself. That means padding can be used + * to visually align/separate an inlay hint. + */ + PaddingRight bool `json:"paddingRight,omitempty"` + /* + * A data entry field that is preserved on an inlay hint between + * a `textDocument/inlayHint` and a `inlayHint/resolve` request. + */ + Data interface{} `json:"data,omitempty"` +} + +/* + * Inlay hint client capabilities. + * + * @since 3.17.0 + */ +type InlayHintClientCapabilities struct { // line 12395 + // Whether inlay hints support dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * Indicates which properties a client can resolve lazily on an inlay + * hint. + */ + ResolveSupport *PResolveSupportPInlayHint `json:"resolveSupport,omitempty"` +} +type InlayHintKind uint32 // line 13059 +/* + * An inlay hint label part allows for interactive and composite labels + * of inlay hints. + * + * @since 3.17.0 + */ +type InlayHintLabelPart struct { // line 7067 + // The value of this label part. + Value string `json:"value"` + /* + * The tooltip text when you hover over this label part. Depending on + * the client capability `inlayHint.resolveSupport` clients might resolve + * this property late using the resolve request. + */ + Tooltip *OrPTooltipPLabel `json:"tooltip,omitempty"` + /* + * An optional source code location that represents this + * label part. + * + * The editor will use this location for the hover and for code navigation + * features: This part will become a clickable link that resolves to the + * definition of the symbol at the given location (not necessarily the + * location itself), it shows the hover that shows at the given location, + * and it shows a context menu with further code navigation commands. + * + * Depending on the client capability `inlayHint.resolveSupport` clients + * might resolve this property late using the resolve request. + */ + Location *Location `json:"location,omitempty"` + /* + * An optional command for this label part. + * + * Depending on the client capability `inlayHint.resolveSupport` clients + * might resolve this property late using the resolve request. + */ + Command *Command `json:"command,omitempty"` +} + +/* + * Inlay hint options used during static registration. + * + * @since 3.17.0 + */ +type InlayHintOptions struct { // line 7140 + /* + * The server provides support to resolve additional + * information for an inlay hint item. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +/* + * A parameter literal used in inlay hint requests. + * + * @since 3.17.0 + */ +type InlayHintParams struct { // line 3638 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The document range for which inlay hints should be computed. + Range Range `json:"range"` + WorkDoneProgressParams +} + +/* + * Inlay hint options used during static or dynamic registration. + * + * @since 3.17.0 + */ +type InlayHintRegistrationOptions struct { // line 3768 + InlayHintOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} + +/* + * Client workspace capabilities specific to inlay hints. + * + * @since 3.17.0 + */ +type InlayHintWorkspaceClientCapabilities struct { // line 11121 + /* + * Whether the client implementation supports a refresh request sent from + * the server to the client. + * + * Note that this event is global and will force the client to refresh all + * inlay hints currently shown. It should be used with absolute care and + * is useful for situation where a server for example detects a project wide + * change that requires such a calculation. + */ + RefreshSupport bool `json:"refreshSupport,omitempty"` +} + +/* + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + * + * @since 3.17.0 + */ +type InlineValue = Or_InlineValue // (alias) line 13887 +/* + * Client capabilities specific to inline values. + * + * @since 3.17.0 + */ +type InlineValueClientCapabilities struct { // line 12379 + // Whether implementation supports dynamic registration for inline value providers. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// @since 3.17.0 +type InlineValueContext struct { // line 6953 + // The stack frame (as a DAP Id) where the execution has stopped. + FrameID int32 `json:"frameId"` + /* + * The document range where execution has stopped. + * Typically the end position of the range denotes the line where the inline values are shown. + */ + StoppedLocation Range `json:"stoppedLocation"` +} + +/* + * Provide an inline value through an expression evaluation. + * If only a range is specified, the expression will be extracted from the underlying document. + * An optional expression can be used to override the extracted expression. + * + * @since 3.17.0 + */ +type InlineValueEvaluatableExpression struct { // line 7031 + /* + * The document range for which the inline value applies. + * The range is used to extract the evaluatable expression from the underlying document. + */ + Range Range `json:"range"` + // If specified the expression overrides the extracted expression. + Expression string `json:"expression,omitempty"` +} + +/* + * Inline value options used during static registration. + * + * @since 3.17.0 + */ +type InlineValueOptions struct { // line 7055 + WorkDoneProgressOptions +} + +/* + * A parameter literal used in inline value requests. + * + * @since 3.17.0 + */ +type InlineValueParams struct { // line 3579 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The document range for which inline values should be computed. + Range Range `json:"range"` + /* + * Additional information about the context in which inline values were + * requested. + */ + Context InlineValueContext `json:"context"` + WorkDoneProgressParams +} + +/* + * Inline value options used during static or dynamic registration. + * + * @since 3.17.0 + */ +type InlineValueRegistrationOptions struct { // line 3616 + InlineValueOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} + +/* + * Provide inline value as text. + * + * @since 3.17.0 + */ +type InlineValueText struct { // line 6976 + // The document range for which the inline value applies. + Range Range `json:"range"` + // The text of the inline value. + Text string `json:"text"` +} + +/* + * Provide inline value through a variable lookup. + * If only a range is specified, the variable name will be extracted from the underlying document. + * An optional variable name can be used to override the extracted name. + * + * @since 3.17.0 + */ +type InlineValueVariableLookup struct { // line 6999 + /* + * The document range for which the inline value applies. + * The range is used to extract the variable name from the underlying document. + */ + Range Range `json:"range"` + // If specified the name of the variable to look up. + VariableName string `json:"variableName,omitempty"` + // How to perform the lookup. + CaseSensitiveLookup bool `json:"caseSensitiveLookup"` +} + +/* + * Client workspace capabilities specific to inline values. + * + * @since 3.17.0 + */ +type InlineValueWorkspaceClientCapabilities struct { // line 11105 + /* + * Whether the client implementation supports a refresh request sent from the + * server to the client. + * + * Note that this event is global and will force the client to refresh all + * inline values currently shown. It should be used with absolute care and is + * useful for situation where a server for example detects a project wide + * change that requires such a calculation. + */ + RefreshSupport bool `json:"refreshSupport,omitempty"` +} + +/* + * A special text edit to provide an insert and a replace operation. + * + * @since 3.16.0 + */ +type InsertReplaceEdit struct { // line 8696 + // The string to be inserted. + NewText string `json:"newText"` + // The range if the insert is requested + Insert Range `json:"insert"` + // The range if the replace is requested. + Replace Range `json:"replace"` +} +type InsertTextFormat uint32 // line 13286 +type InsertTextMode uint32 // line 13306 +/* + * The LSP any type. + * Please note that strictly speaking a property with the value `undefined` + * can't be converted into JSON preserving the property name. However for + * convenience it is allowed and assumed that all these properties are + * optional as well. + * @since 3.17.0 + */ +type LSPAny = interface{} // (alias) line 13817 +/* + * LSP arrays. + * @since 3.17.0 + */ +type LSPArray = []interface{} // (alias) line 13805 +type LSPErrorCodes int32 // line 12809 +/* + * LSP object definition. + * @since 3.17.0 + */ +type LSPObject struct { // line 9618 +} + +/* + * Client capabilities for the linked editing range request. + * + * @since 3.16.0 + */ +type LinkedEditingRangeClientCapabilities struct { // line 12331 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} +type LinkedEditingRangeOptions struct { // line 6657 + WorkDoneProgressOptions +} +type LinkedEditingRangeParams struct { // line 3134 + TextDocumentPositionParams + WorkDoneProgressParams +} +type LinkedEditingRangeRegistrationOptions struct { // line 3177 + TextDocumentRegistrationOptions + LinkedEditingRangeOptions + StaticRegistrationOptions +} + +/* + * The result of a linked editing range request. + * + * @since 3.16.0 + */ +type LinkedEditingRanges struct { // line 3150 + /* + * A list of ranges that can be edited together. The ranges must have + * identical length and contain identical text content. The ranges cannot overlap. + */ + Ranges []Range `json:"ranges"` + /* + * An optional word pattern (regular expression) that describes valid contents for + * the given ranges. If no pattern is provided, the client configuration's word + * pattern will be used. + */ + WordPattern string `json:"wordPattern,omitempty"` +} + +/* + * Represents a location inside a resource, such as a line + * inside a text file. + */ +type Location struct { // line 2091 + URI DocumentURI `json:"uri"` + Range Range `json:"range"` +} + +/* + * Represents the connection of two locations. Provides additional metadata over normal [locations](#Location), + * including an origin range. + */ +type LocationLink struct { // line 6277 + /* + * Span of the origin of this link. + * + * Used as the underlined span for mouse interaction. Defaults to the word range at + * the definition position. + */ + OriginSelectionRange *Range `json:"originSelectionRange,omitempty"` + // The target resource identifier of this link. + TargetURI DocumentURI `json:"targetUri"` + /* + * The full target range of this link. If the target for example is a symbol then target range is the + * range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to highlight the range in the editor. + */ + TargetRange Range `json:"targetRange"` + /* + * The range that should be selected and revealed when this link is being followed, e.g the name of a function. + * Must be contained by the `targetRange`. See also `DocumentSymbol#range` + */ + TargetSelectionRange Range `json:"targetSelectionRange"` +} + +// The log message parameters. +type LogMessageParams struct { // line 4273 + // The message type. See {@link MessageType} + Type MessageType `json:"type"` + // The actual message. + Message string `json:"message"` +} +type LogTraceParams struct { // line 6178 + Message string `json:"message"` + Verbose string `json:"verbose,omitempty"` +} + +/* + * Client capabilities specific to the used markdown parser. + * + * @since 3.16.0 + */ +type MarkdownClientCapabilities struct { // line 12550 + // The name of the parser. + Parser string `json:"parser"` + // The version of the parser. + Version string `json:"version,omitempty"` + /* + * A list of HTML tags that the client allows / supports in + * Markdown. + * + * @since 3.17.0 + */ + AllowedTags []string `json:"allowedTags,omitempty"` +} + +/* + * MarkedString can be used to render human readable text. It is either a markdown string + * or a code-block that provides a language and a code snippet. The language identifier + * is semantically equal to the optional language identifier in fenced code blocks in GitHub + * issues. See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting + * + * The pair of a language and a value is an equivalent to markdown: + * ```${language} + * ${value} + * ``` + * + * Note that markdown strings will be sanitized - that means html will be escaped. + * @deprecated use MarkupContent instead. + */ +type MarkedString = Or_MarkedString // (alias) line 14084 +/* + * A `MarkupContent` literal represents a string value which content is interpreted base on its + * kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. + * + * If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues. + * See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting + * + * Here is an example how such a string can be constructed using JavaScript / TypeScript: + * ```ts + * let markdown: MarkdownContent = { + * kind: MarkupKind.Markdown, + * value: [ + * '# Header', + * 'Some text', + * '```typescript', + * 'someCode();', + * '```' + * ].join('\ + * ') + * }; + * ``` + * + * *Please Note* that clients might sanitize the return markdown. A client could decide to + * remove HTML from the markdown to avoid script execution. + */ +type MarkupContent struct { // line 7118 + // The type of the Markup + Kind MarkupKind `json:"kind"` + // The content itself + Value string `json:"value"` +} +type MarkupKind string // line 13433 +type MessageActionItem struct { // line 4260 + // A short title like 'Retry', 'Open Log' etc. + Title string `json:"title"` +} +type MessageType uint32 // line 13080 +/* + * Moniker definition to match LSIF 0.5 moniker definition. + * + * @since 3.16.0 + */ +type Moniker struct { // line 3360 + // The scheme of the moniker. For example tsc or .Net + Scheme string `json:"scheme"` + /* + * The identifier of the moniker. The value is opaque in LSIF however + * schema owners are allowed to define the structure if they want. + */ + Identifier string `json:"identifier"` + // The scope in which the moniker is unique + Unique UniquenessLevel `json:"unique"` + // The moniker kind if known. + Kind MonikerKind `json:"kind,omitempty"` +} + +/* + * Client capabilities specific to the moniker request. + * + * @since 3.16.0 + */ +type MonikerClientCapabilities struct { // line 12347 + /* + * Whether moniker supports dynamic registration. If this is set to `true` + * the client supports the new `MonikerRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} +type MonikerKind string // line 13033 +type MonikerOptions struct { // line 6931 + WorkDoneProgressOptions +} +type MonikerParams struct { // line 3340 + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} +type MonikerRegistrationOptions struct { // line 3400 + TextDocumentRegistrationOptions + MonikerOptions +} + +// created for Literal +type Msg_MarkedString struct { // line 14093 + Language string `json:"language"` + Value string `json:"value"` +} + +// created for Literal +type Msg_NotebookDocumentFilter struct { // line 14268 + // The type of the enclosing notebook. + NotebookType string `json:"notebookType"` + // A Uri [scheme](#Uri.scheme), like `file` or `untitled`. + Scheme string `json:"scheme"` + // A glob pattern. + Pattern string `json:"pattern"` +} + +// created for Literal +type Msg_PrepareRename2Gn struct { // line 13936 + Range Range `json:"range"` + Placeholder string `json:"placeholder"` +} + +// created for Literal +type Msg_TextDocumentContentChangeEvent struct { // line 14033 + // The range of the document that changed. + Range *Range `json:"range"` + /* + * The optional length of the range that got replaced. + * + * @deprecated use range instead. + */ + RangeLength uint32 `json:"rangeLength"` + // The new text for the provided range. + Text string `json:"text"` +} + +// created for Literal +type Msg_TextDocumentFilter struct { // line 14159 + // A language id, like `typescript`. + Language string `json:"language"` + // A Uri [scheme](#Uri.scheme), like `file` or `untitled`. + Scheme string `json:"scheme"` + // A glob pattern, like `*.{ts,js}`. + Pattern string `json:"pattern"` +} + +// created for Literal +type Msg_XInitializeParams_clientInfo struct { // line 7678 + // The name of the client as defined by the client. + Name string `json:"name"` + // The client's version as defined by the client. + Version string `json:"version"` +} + +/* + * A notebook cell. + * + * A cell's document URI must be unique across ALL notebook + * cells and can therefore be used to uniquely identify a + * notebook cell or the cell's text document. + * + * @since 3.17.0 + */ +type NotebookCell struct { // line 9624 + // The cell's kind + Kind NotebookCellKind `json:"kind"` + /* + * The URI of the cell's text document + * content. + */ + Document DocumentURI `json:"document"` + /* + * Additional metadata stored with the cell. + * + * Note: should always be an object literal (e.g. LSPObject) + */ + Metadata *LSPObject `json:"metadata,omitempty"` + /* + * Additional execution summary information + * if supported by the client. + */ + ExecutionSummary *ExecutionSummary `json:"executionSummary,omitempty"` +} + +/* + * A change describing how to move a `NotebookCell` + * array from state S to S'. + * + * @since 3.17.0 + */ +type NotebookCellArrayChange struct { // line 9665 + // The start oftest of the cell that changed. + Start uint32 `json:"start"` + // The deleted cells + DeleteCount uint32 `json:"deleteCount"` + // The new cells, if any + Cells []NotebookCell `json:"cells,omitempty"` +} +type NotebookCellKind uint32 // line 13674 +/* + * A notebook cell text document filter denotes a cell text + * document by different properties. + * + * @since 3.17.0 + */ +type NotebookCellTextDocumentFilter struct { // line 10139 + /* + * A filter that matches against the notebook + * containing the notebook cell. If a string + * value is provided it matches against the + * notebook type. '*' matches every notebook. + */ + Notebook NotebookDocumentFilter `json:"notebook"` + /* + * A language id like `python`. + * + * Will be matched against the language id of the + * notebook cell document. '*' matches every language. + */ + Language string `json:"language,omitempty"` +} + +/* + * A notebook document. + * + * @since 3.17.0 + */ +type NotebookDocument struct { // line 7359 + // The notebook document's uri. + URI URI `json:"uri"` + // The type of the notebook. + NotebookType string `json:"notebookType"` + /* + * The version number of this document (it will increase after each + * change, including undo/redo). + */ + Version int32 `json:"version"` + /* + * Additional metadata stored with the notebook + * document. + * + * Note: should always be an object literal (e.g. LSPObject) + */ + Metadata *LSPObject `json:"metadata,omitempty"` + // The cells of a notebook. + Cells []NotebookCell `json:"cells"` +} + +/* + * A change event for a notebook document. + * + * @since 3.17.0 + */ +type NotebookDocumentChangeEvent struct { // line 7471 + /* + * The changed meta data if any. + * + * Note: should always be an object literal (e.g. LSPObject) + */ + Metadata *LSPObject `json:"metadata,omitempty"` + // Changes to cells + Cells *PCellsPChange `json:"cells,omitempty"` +} + +/* + * Capabilities specific to the notebook document support. + * + * @since 3.17.0 + */ +type NotebookDocumentClientCapabilities struct { // line 10639 + /* + * Capabilities specific to notebook document synchronization + * + * @since 3.17.0 + */ + Synchronization NotebookDocumentSyncClientCapabilities `json:"synchronization"` +} + +/* + * A notebook document filter denotes a notebook document by + * different properties. The properties will be match + * against the notebook's URI (same as with documents) + * + * @since 3.17.0 + */ +type NotebookDocumentFilter = Msg_NotebookDocumentFilter // (alias) line 14263 +/* + * A literal to identify a notebook document in the client. + * + * @since 3.17.0 + */ +type NotebookDocumentIdentifier struct { // line 7587 + // The notebook document's uri. + URI URI `json:"uri"` +} + +/* + * Notebook specific client capabilities. + * + * @since 3.17.0 + */ +type NotebookDocumentSyncClientCapabilities struct { // line 12459 + /* + * Whether implementation supports dynamic registration. If this is + * set to `true` the client supports the new + * `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + // The client supports sending execution summary data per cell. + ExecutionSummarySupport bool `json:"executionSummarySupport,omitempty"` +} + +/* + * Options specific to a notebook plus its cells + * to be synced to the server. + * + * If a selector provides a notebook document + * filter but no cell selector all cells of a + * matching notebook document will be synced. + * + * If a selector provides no notebook document + * filter but only a cell selector all notebook + * document that contain at least one matching + * cell will be synced. + * + * @since 3.17.0 + */ +type NotebookDocumentSyncOptions struct { // line 9821 + // The notebooks to be synced + NotebookSelector []PNotebookSelectorPNotebookDocumentSync `json:"notebookSelector"` + /* + * Whether save notification should be forwarded to + * the server. Will only be honored if mode === `notebook`. + */ + Save bool `json:"save,omitempty"` +} + +/* + * Registration options specific to a notebook. + * + * @since 3.17.0 + */ +type NotebookDocumentSyncRegistrationOptions struct { // line 9941 + NotebookDocumentSyncOptions + StaticRegistrationOptions +} + +// A text document identifier to optionally denote a specific version of a text document. +type OptionalVersionedTextDocumentIdentifier struct { // line 9363 + /* + * The version number of this document. If a versioned text document identifier + * is sent from the server to the client and the file is not open in the editor + * (the server has not received an open notification before) the server can send + * `null` to indicate that the version is unknown and the content on disk is the + * truth (as specified with document content ownership). + */ + Version int32 `json:"version"` + TextDocumentIdentifier +} + +// created for Or [Range FEditRangePItemDefaults] +type OrFEditRangePItemDefaults struct { // line 4791 + Value interface{} `json:"value"` +} + +// created for Or [string NotebookDocumentFilter] +type OrFNotebookPNotebookSelector struct { // line 9838 + Value interface{} `json:"value"` +} + +// created for Or [Location PLocationMsg_workspace_symbol] +type OrPLocation_workspace_symbol struct { // line 5540 + Value interface{} `json:"value"` +} + +// created for Or [string []string] +type OrPSection_workspace_didChangeConfiguration struct { // line 4186 + Value interface{} `json:"value"` +} + +// created for Or [string MarkupContent] +type OrPTooltipPLabel struct { // line 7081 + Value interface{} `json:"value"` +} + +// created for Or [string MarkupContent] +type OrPTooltip_textDocument_inlayHint struct { // line 3722 + Value interface{} `json:"value"` +} + +// created for Or [Location []Location] +type Or_Definition struct { // line 13780 + Value interface{} `json:"value"` +} + +// created for Or [RelatedFullDocumentDiagnosticReport RelatedUnchangedDocumentDiagnosticReport] +type Or_DocumentDiagnosticReport struct { // line 13912 + Value interface{} `json:"value"` +} + +// created for Or [TextDocumentFilter NotebookCellTextDocumentFilter] +type Or_DocumentFilter struct { // line 14121 + Value interface{} `json:"value"` +} + +// created for Or [InlineValueText InlineValueVariableLookup InlineValueEvaluatableExpression] +type Or_InlineValue struct { // line 13890 + Value interface{} `json:"value"` +} + +// created for Or [string Msg_MarkedString] +type Or_MarkedString struct { // line 14087 + Value interface{} `json:"value"` +} + +// created for Or [WorkspaceFolder URI] +type Or_RelativePattern_baseUri struct { // line 10768 + Value interface{} `json:"value"` +} + +// created for Or [WorkspaceFullDocumentDiagnosticReport WorkspaceUnchangedDocumentDiagnosticReport] +type Or_WorkspaceDocumentDiagnosticReport struct { // line 14013 + Value interface{} `json:"value"` +} + +// created for Or [Declaration []DeclarationLink ] +type Or_textDocument_declaration struct { // line 257 + Value interface{} `json:"value"` +} + +// created for Literal +type PCellsPChange struct { // line 7486 + /* + * Changes to the cell structure to add or + * remove cells. + */ + Structure FStructurePCells `json:"structure"` + /* + * Changes to notebook cells properties like its + * kind, execution summary or metadata. + */ + Data []NotebookCell `json:"data"` + // Changes to the text content of notebook cells. + TextContent []FTextContentPCells `json:"textContent"` +} + +// created for Literal +type PChangeAnnotationSupportPWorkspaceEdit struct { // line 10842 + /* + * Whether the client groups edits with equal labels into tree nodes, + * for instance all edits labelled with \"Changes in Strings\" would + * be a tree node. + */ + GroupsOnLabel bool `json:"groupsOnLabel"` +} + +// created for Literal +type PCodeActionLiteralSupportPCodeAction struct { // line 11762 + /* + * The code action kind is support with the following value + * set. + */ + CodeActionKind FCodeActionKindPCodeActionLiteralSupport `json:"codeActionKind"` +} + +// created for Literal +type PCompletionItemKindPCompletion struct { // line 11360 + /* + * The completion item kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the completion items kinds from `Text` to `Reference` as defined in + * the initial version of the protocol. + */ + ValueSet []CompletionItemKind `json:"valueSet"` +} + +// created for Literal +type PCompletionItemPCompletion struct { // line 11209 + /* + * Client supports snippets as insert text. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Placeholders with equal identifiers are linked, + * that is typing in one will update others too. + */ + SnippetSupport bool `json:"snippetSupport"` + // Client supports commit characters on a completion item. + CommitCharactersSupport bool `json:"commitCharactersSupport"` + /* + * Client supports the following content formats for the documentation + * property. The order describes the preferred format of the client. + */ + DocumentationFormat []MarkupKind `json:"documentationFormat"` + // Client supports the deprecated property on a completion item. + DeprecatedSupport bool `json:"deprecatedSupport"` + // Client supports the preselect property on a completion item. + PreselectSupport bool `json:"preselectSupport"` + /* + * Client supports the tag property on a completion item. Clients supporting + * tags have to handle unknown tags gracefully. Clients especially need to + * preserve unknown tags when sending a completion item back to the server in + * a resolve call. + * + * @since 3.15.0 + */ + TagSupport FTagSupportPCompletionItem `json:"tagSupport"` + /* + * Client support insert replace edit to control different behavior if a + * completion item is inserted in the text or should replace text. + * + * @since 3.16.0 + */ + InsertReplaceSupport bool `json:"insertReplaceSupport"` + /* + * Indicates which properties a client can resolve lazily on a completion + * item. Before version 3.16.0 only the predefined properties `documentation` + * and `details` could be resolved lazily. + * + * @since 3.16.0 + */ + ResolveSupport FResolveSupportPCompletionItem `json:"resolveSupport"` + /* + * The client supports the `insertTextMode` property on + * a completion item to override the whitespace handling mode + * as defined by the client (see `insertTextMode`). + * + * @since 3.16.0 + */ + InsertTextModeSupport FInsertTextModeSupportPCompletionItem `json:"insertTextModeSupport"` + /* + * The client has support for completion item label + * details (see also `CompletionItemLabelDetails`). + * + * @since 3.17.0 + */ + LabelDetailsSupport bool `json:"labelDetailsSupport"` +} + +// created for Literal +type PCompletionItemPCompletionProvider struct { // line 8767 + /* + * The server has support for completion item label + * details (see also `CompletionItemLabelDetails`) when + * receiving a completion item in a resolve call. + * + * @since 3.17.0 + */ + LabelDetailsSupport bool `json:"labelDetailsSupport"` +} + +// created for Literal +type PCompletionListPCompletion struct { // line 11402 + /* + * The client supports the following itemDefaults on + * a completion list. + * + * The value lists the supported property names of the + * `CompletionList.itemDefaults` object. If omitted + * no properties are supported. + * + * @since 3.17.0 + */ + ItemDefaults []string `json:"itemDefaults"` +} + +// created for Literal +type PDisabledMsg_textDocument_codeAction struct { // line 5446 + /* + * Human readable description of why the code action is currently disabled. + * + * This is displayed in the code actions UI. + */ + Reason string `json:"reason"` +} + +// created for Literal +type PFoldingRangeKindPFoldingRange struct { // line 12037 + /* + * The folding range kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + */ + ValueSet []FoldingRangeKind `json:"valueSet"` +} + +// created for Literal +type PFoldingRangePFoldingRange struct { // line 12062 + /* + * If set, the client signals that it supports setting collapsedText on + * folding ranges to display custom labels instead of the default text. + * + * @since 3.17.0 + */ + CollapsedText bool `json:"collapsedText"` +} + +// created for Literal +type PFullESemanticTokensOptions struct { // line 6591 + // The server supports deltas for full documents. + Delta bool `json:"delta"` +} + +// created for Literal +type PItemDefaultsMsg_textDocument_completion struct { // line 4772 + /* + * A default commit character set. + * + * @since 3.17.0 + */ + CommitCharacters []string `json:"commitCharacters"` + /* + * A default edit range. + * + * @since 3.17.0 + */ + EditRange OrFEditRangePItemDefaults `json:"editRange"` + /* + * A default insert text format. + * + * @since 3.17.0 + */ + InsertTextFormat InsertTextFormat `json:"insertTextFormat"` + /* + * A default insert text mode. + * + * @since 3.17.0 + */ + InsertTextMode InsertTextMode `json:"insertTextMode"` + /* + * A default data value. + * + * @since 3.17.0 + */ + Data interface{} `json:"data"` +} + +// created for Literal +type PLocationMsg_workspace_symbol struct { // line 5546 + URI DocumentURI `json:"uri"` +} + +// created for Literal +type PMessageActionItemPShowMessage struct { // line 12490 + /* + * Whether the client supports additional attributes which + * are preserved and send back to the server in the + * request's response. + */ + AdditionalPropertiesSupport bool `json:"additionalPropertiesSupport"` +} + +// created for Literal +type PNotebookSelectorPNotebookDocumentSync struct { // line 9831 + /* + * The notebook to be synced If a string + * value is provided it matches against the + * notebook type. '*' matches every notebook. + */ + Notebook OrFNotebookPNotebookSelector `json:"notebook"` + // The cells of the matching notebook to be synced. + Cells []FCellsPNotebookSelector `json:"cells"` +} + +// created for Literal +type PRangeESemanticTokensOptions struct { // line 6571 +} + +// created for Literal +type PRequestsPSemanticTokens struct { // line 12198 + /* + * The client will send the `textDocument/semanticTokens/range` request if + * the server provides a corresponding handler. + */ + Range bool `json:"range"` + /* + * The client will send the `textDocument/semanticTokens/full` request if + * the server provides a corresponding handler. + */ + Full interface{} `json:"full"` +} + +// created for Literal +type PResolveSupportPCodeAction struct { // line 11827 + // The properties that a client can resolve lazily. + Properties []string `json:"properties"` +} + +// created for Literal +type PResolveSupportPInlayHint struct { // line 12410 + // The properties that a client can resolve lazily. + Properties []string `json:"properties"` +} + +// created for Literal +type PResolveSupportPSymbol struct { // line 10964 + /* + * The properties that a client can resolve lazily. Usually + * `location.range` + */ + Properties []string `json:"properties"` +} + +// created for Literal +type PServerInfoMsg_initialize struct { // line 4118 + // The name of the server as defined by the server. + Name string `json:"name"` + // The server's version as defined by the server. + Version string `json:"version"` +} + +// created for Literal +type PSignatureInformationPSignatureHelp struct { // line 11469 + /* + * Client supports the following content formats for the documentation + * property. The order describes the preferred format of the client. + */ + DocumentationFormat []MarkupKind `json:"documentationFormat"` + // Client capabilities specific to parameter information. + ParameterInformation FParameterInformationPSignatureInformation `json:"parameterInformation"` + /* + * The client supports the `activeParameter` property on `SignatureInformation` + * literal. + * + * @since 3.16.0 + */ + ActiveParameterSupport bool `json:"activeParameterSupport"` +} + +// created for Literal +type PStaleRequestSupportPGeneral struct { // line 10696 + // The client will actively cancel the request. + Cancel bool `json:"cancel"` + /* + * The list of requests for which the client + * will retry the request if it receives a + * response with error code `ContentModified` + */ + RetryOnContentModified []string `json:"retryOnContentModified"` +} + +// created for Literal +type PSymbolKindPDocumentSymbol struct { // line 11680 + /* + * The symbol kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the symbol kinds from `File` to `Array` as defined in + * the initial version of the protocol. + */ + ValueSet []SymbolKind `json:"valueSet"` +} + +// created for Literal +type PSymbolKindPSymbol struct { // line 10916 + /* + * The symbol kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the symbol kinds from `File` to `Array` as defined in + * the initial version of the protocol. + */ + ValueSet []SymbolKind `json:"valueSet"` +} + +// created for Literal +type PTagSupportPDocumentSymbol struct { // line 11713 + // The tags supported by the client. + ValueSet []SymbolTag `json:"valueSet"` +} + +// created for Literal +type PTagSupportPPublishDiagnostics struct { // line 12113 + // The tags supported by the client. + ValueSet []DiagnosticTag `json:"valueSet"` +} + +// created for Literal +type PTagSupportPSymbol struct { // line 10940 + // The tags supported by the client. + ValueSet []SymbolTag `json:"valueSet"` +} + +// The parameters of a configuration request. +type ParamConfiguration struct { // line 2207 + Items []ConfigurationItem `json:"items"` +} +type ParamInitialize struct { // line 4090 + XInitializeParams + WorkspaceFoldersInitializeParams +} + +/* + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ +type ParameterInformation struct { // line 10089 + /* + * The label of this parameter information. + * + * Either a string or an inclusive start and exclusive end offsets within its containing + * signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + * string representation as `Position` and `Range` does. + * + * *Note*: a label of type string should be a substring of its containing signature label. + * Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + */ + Label string `json:"label"` + /* + * The human-readable doc-comment of this parameter. Will be shown + * in the UI but can be omitted. + */ + Documentation string `json:"documentation,omitempty"` +} +type PartialResultParams struct { // line 2223 + /* + * An optional token that a server can use to report partial results (e.g. streaming) to + * the client. + */ + PartialResultToken ProgressToken `json:"partialResultToken,omitempty"` +} + +/* + * The glob pattern to watch relative to the base path. Glob patterns can have the following syntax: + * - `*` to match one or more characters in a path segment + * - `?` to match on one character in a path segment + * - `**` to match any number of path segments, including none + * - `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + * + * @since 3.17.0 + */ +type Pattern = string // (alias) line 14372 +/* + * Position in a text document expressed as zero-based line and character + * offset. Prior to 3.17 the offsets were always based on a UTF-16 string + * representation. So a string of the form `a𐐀b` the character offset of the + * character `a` is 0, the character offset of `𐐀` is 1 and the character + * offset of b is 3 since `𐐀` is represented using two code units in UTF-16. + * Since 3.17 clients and servers can agree on a different string encoding + * representation (e.g. UTF-8). The client announces it's supported encoding + * via the client capability [`general.positionEncodings`](#clientCapabilities). + * The value is an array of position encodings the client supports, with + * decreasing preference (e.g. the encoding at index `0` is the most preferred + * one). To stay backwards compatible the only mandatory encoding is UTF-16 + * represented via the string `utf-16`. The server can pick one of the + * encodings offered by the client and signals that encoding back to the + * client via the initialize result's property + * [`capabilities.positionEncoding`](#serverCapabilities). If the string value + * `utf-16` is missing from the client's capability `general.positionEncodings` + * servers can safely assume that the client supports UTF-16. If the server + * omits the position encoding in its initialize result the encoding defaults + * to the string value `utf-16`. Implementation considerations: since the + * conversion from one encoding into another requires the content of the + * file / line the conversion is best done where the file is read which is + * usually on the server side. + * + * Positions are line end character agnostic. So you can not specify a position + * that denotes `\\r|\ + * ` or `\ + * |` where `|` represents the character offset. + * + * @since 3.17.0 - support for negotiated position encoding. + */ +type Position struct { // line 6506 + /* + * Line position in a document (zero-based). + * + * If a line number is greater than the number of lines in a document, it defaults back to the number of lines in the document. + * If a line number is negative, it defaults to 0. + */ + Line uint32 `json:"line"` + /* + * Character offset on a line in a document (zero-based). + * + * The meaning of this offset is determined by the negotiated + * `PositionEncodingKind`. + * + * If the character value is greater than the line length it defaults back to the + * line length. + */ + Character uint32 `json:"character"` +} +type PositionEncodingKind string // line 13453 +type PrepareRename2Gn = Msg_PrepareRename2Gn // (alias) line 13927 +type PrepareRenameParams struct { // line 5944 + TextDocumentPositionParams + WorkDoneProgressParams +} +type PrepareRenameResult = Msg_PrepareRename2Gn // (alias) line 13927 +type PrepareSupportDefaultBehavior interface{} // line 13748 +/* + * A previous result id in a workspace pull request. + * + * @since 3.17.0 + */ +type PreviousResultID struct { // line 7336 + /* + * The URI for which the client knowns a + * result id. + */ + URI DocumentURI `json:"uri"` + // The value of the previous result id. + Value string `json:"value"` +} + +/* + * A previous result id in a workspace pull request. + * + * @since 3.17.0 + */ +type PreviousResultId struct { // line 7336 + /* + * The URI for which the client knowns a + * result id. + */ + URI DocumentURI `json:"uri"` + // The value of the previous result id. + Value string `json:"value"` +} +type ProgressParams struct { // line 6220 + // The progress token provided by the client or server. + Token ProgressToken `json:"token"` + // The progress data. + Value interface{} `json:"value"` +} +type ProgressToken = interface{} // (alias) line 13974 +// The publish diagnostic client capabilities. +type PublishDiagnosticsClientCapabilities struct { // line 12098 + // Whether the clients accepts diagnostics with related information. + RelatedInformation bool `json:"relatedInformation,omitempty"` + /* + * Client supports the tag property to provide meta data about a diagnostic. + * Clients supporting tags have to handle unknown tags gracefully. + * + * @since 3.15.0 + */ + TagSupport *PTagSupportPPublishDiagnostics `json:"tagSupport,omitempty"` + /* + * Whether the client interprets the version property of the + * `textDocument/publishDiagnostics` notification's parameter. + * + * @since 3.15.0 + */ + VersionSupport bool `json:"versionSupport,omitempty"` + /* + * Client supports a codeDescription property + * + * @since 3.16.0 + */ + CodeDescriptionSupport bool `json:"codeDescriptionSupport,omitempty"` + /* + * Whether code action supports the `data` property which is + * preserved between a `textDocument/publishDiagnostics` and + * `textDocument/codeAction` request. + * + * @since 3.16.0 + */ + DataSupport bool `json:"dataSupport,omitempty"` +} + +// The publish diagnostic notification's parameters. +type PublishDiagnosticsParams struct { // line 4484 + // The URI for which diagnostic information is reported. + URI DocumentURI `json:"uri"` + /* + * Optional the version number of the document the diagnostics are published for. + * + * @since 3.15.0 + */ + Version int32 `json:"version,omitempty"` + // An array of diagnostic information items. + Diagnostics []Diagnostic `json:"diagnostics"` +} + +/* + * A range in a text document expressed as (zero-based) start and end positions. + * + * If you want to specify a range that contains a line including the line ending + * character(s) then use an end position denoting the start of the next line. + * For example: + * ```ts + * { + * start: { line: 5, character: 23 } + * end : { line 6, character : 0 } + * } + * ``` + */ +type Range struct { // line 6316 + // The range's start position. + Start Position `json:"start"` + // The range's end position. + End Position `json:"end"` +} + +// Client Capabilities for a [ReferencesRequest](#ReferencesRequest). +type ReferenceClientCapabilities struct { // line 11635 + // Whether references supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/* + * Value-object that contains additional information when + * requesting references. + */ +type ReferenceContext struct { // line 8950 + // Include the declaration of the current symbol. + IncludeDeclaration bool `json:"includeDeclaration"` +} + +// Reference options. +type ReferenceOptions struct { // line 8964 + WorkDoneProgressOptions +} + +// Parameters for a [ReferencesRequest](#ReferencesRequest). +type ReferenceParams struct { // line 5075 + Context ReferenceContext `json:"context"` + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [ReferencesRequest](#ReferencesRequest). +type ReferenceRegistrationOptions struct { // line 5104 + TextDocumentRegistrationOptions + ReferenceOptions +} + +// General parameters to to register for an notification or to register a provider. +type Registration struct { // line 7602 + /* + * The id used to register the request. The id can be used to deregister + * the request again. + */ + ID string `json:"id"` + // The method / capability to register for. + Method string `json:"method"` + // Options necessary for the registration. + RegisterOptions interface{} `json:"registerOptions,omitempty"` +} +type RegistrationParams struct { // line 4060 + Registrations []Registration `json:"registrations"` +} + +/* + * Client capabilities specific to regular expressions. + * + * @since 3.16.0 + */ +type RegularExpressionsClientCapabilities struct { // line 12526 + // The engine's name. + Engine string `json:"engine"` + // The engine's version. + Version string `json:"version,omitempty"` +} + +/* + * A full diagnostic report with a set of related documents. + * + * @since 3.17.0 + */ +type RelatedFullDocumentDiagnosticReport struct { // line 7162 + /* + * Diagnostics of related documents. This information is useful + * in programming languages where code in a file A can generate + * diagnostics in a file B which A depends on. An example of + * such a language is C/C++ where marco definitions in a file + * a.cpp and result in errors in a header file b.hpp. + * + * @since 3.17.0 + */ + RelatedDocuments map[DocumentURI]interface{} `json:"relatedDocuments,omitempty"` + FullDocumentDiagnosticReport +} + +/* + * An unchanged diagnostic report with a set of related documents. + * + * @since 3.17.0 + */ +type RelatedUnchangedDocumentDiagnosticReport struct { // line 7201 + /* + * Diagnostics of related documents. This information is useful + * in programming languages where code in a file A can generate + * diagnostics in a file B which A depends on. An example of + * such a language is C/C++ where marco definitions in a file + * a.cpp and result in errors in a header file b.hpp. + * + * @since 3.17.0 + */ + RelatedDocuments map[DocumentURI]interface{} `json:"relatedDocuments,omitempty"` + UnchangedDocumentDiagnosticReport +} + +/* + * A relative pattern is a helper to construct glob patterns that are matched + * relatively to a base URI. The common value for a `baseUri` is a workspace + * folder root, but it can be another absolute URI as well. + * + * @since 3.17.0 + */ +type RelativePattern struct { // line 10762 + /* + * A workspace folder or a base URI to which this pattern will be matched + * against relatively. + */ + BaseURI Or_RelativePattern_baseUri `json:"baseUri"` + // The actual glob pattern; + Pattern Pattern `json:"pattern"` +} +type RenameClientCapabilities struct { // line 11960 + // Whether rename supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * Client supports testing for validity of rename operations + * before execution. + * + * @since 3.12.0 + */ + PrepareSupport bool `json:"prepareSupport,omitempty"` + /* + * Client supports the default behavior result. + * + * The value indicates the default behavior used by the + * client. + * + * @since 3.16.0 + */ + PrepareSupportDefaultBehavior interface{} `json:"prepareSupportDefaultBehavior,omitempty"` + /* + * Whether the client honors the change annotations in + * text edits and resource operations returned via the + * rename request's workspace edit by for example presenting + * the workspace edit in the user interface and asking + * for confirmation. + * + * @since 3.16.0 + */ + HonorsChangeAnnotations bool `json:"honorsChangeAnnotations,omitempty"` +} + +// Rename file operation +type RenameFile struct { // line 6754 + // A rename + Kind string `json:"kind"` + // The old (existing) location. + OldURI DocumentURI `json:"oldUri"` + // The new location. + NewURI DocumentURI `json:"newUri"` + // Rename options. + Options *RenameFileOptions `json:"options,omitempty"` + ResourceOperation +} + +// Rename file options +type RenameFileOptions struct { // line 9461 + // Overwrite target if existing. Overwrite wins over `ignoreIfExists` + Overwrite bool `json:"overwrite,omitempty"` + // Ignores if target exists. + IgnoreIfExists bool `json:"ignoreIfExists,omitempty"` +} + +/* + * The parameters sent in notifications/requests for user-initiated renames of + * files. + * + * @since 3.16.0 + */ +type RenameFilesParams struct { // line 3304 + /* + * An array of all files/folders renamed in this operation. When a folder is renamed, only + * the folder will be included, and not its children. + */ + Files []FileRename `json:"files"` +} + +// Provider options for a [RenameRequest](#RenameRequest). +type RenameOptions struct { // line 9289 + /* + * Renames should be checked and tested before being executed. + * + * @since version 3.12.0 + */ + PrepareProvider bool `json:"prepareProvider,omitempty"` + WorkDoneProgressOptions +} + +// The parameters of a [RenameRequest](#RenameRequest). +type RenameParams struct { // line 5893 + // The document to rename. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The position at which this request was sent. + Position Position `json:"position"` + /* + * The new name of the symbol. If the given name is not valid the + * request must return a [ResponseError](#ResponseError) with an + * appropriate message set. + */ + NewName string `json:"newName"` + WorkDoneProgressParams +} + +// Registration options for a [RenameRequest](#RenameRequest). +type RenameRegistrationOptions struct { // line 5929 + TextDocumentRegistrationOptions + RenameOptions +} + +// A generic resource operation. +type ResourceOperation struct { // line 9413 + // The resource operation kind. + Kind string `json:"kind"` + /* + * An optional annotation identifier describing the operation. + * + * @since 3.16.0 + */ + AnnotationID ChangeAnnotationIdentifier `json:"annotationId,omitempty"` +} +type ResourceOperationKind string // line 13695 +// Save options. +type SaveOptions struct { // line 8485 + // The client is supposed to include the content on save. + IncludeText bool `json:"includeText,omitempty"` +} + +/* + * A selection range represents a part of a selection hierarchy. A selection range + * may have a parent selection range that contains it. + */ +type SelectionRange struct { // line 2591 + // The [range](#Range) of this selection range. + Range Range `json:"range"` + // The parent selection range containing this range. Therefore `parent.range` must contain `this.range`. + Parent *SelectionRange `json:"parent,omitempty"` +} +type SelectionRangeClientCapabilities struct { // line 12084 + /* + * Whether implementation supports dynamic registration for selection range providers. If this is set to `true` + * the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server + * capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} +type SelectionRangeOptions struct { // line 6529 + WorkDoneProgressOptions +} + +// A parameter literal used in selection range requests. +type SelectionRangeParams struct { // line 2556 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The positions inside the text document. + Positions []Position `json:"positions"` + WorkDoneProgressParams + PartialResultParams +} +type SelectionRangeRegistrationOptions struct { // line 2614 + SelectionRangeOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} +type SemanticTokenModifiers string // line 12696 +type SemanticTokenTypes string // line 12589 +// @since 3.16.0 +type SemanticTokens struct { // line 2902 + /* + * An optional result id. If provided and clients support delta updating + * the client will include the result id in the next semantic token request. + * A server can then instead of computing all semantic tokens again simply + * send a delta. + */ + ResultID string `json:"resultId,omitempty"` + // The actual tokens. + Data []uint32 `json:"data"` +} + +// @since 3.16.0 +type SemanticTokensClientCapabilities struct { // line 12183 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * Which requests the client supports and might send to the server + * depending on the server's capability. Please note that clients might not + * show semantic tokens or degrade some of the user experience if a range + * or full request is advertised by the client but not provided by the + * server. If for example the client capability `requests.full` and + * `request.range` are both set to true but the server only provides a + * range provider the client might not render a minimap correctly or might + * even decide to not show any semantic tokens at all. + */ + Requests PRequestsPSemanticTokens `json:"requests"` + // The token types that the client supports. + TokenTypes []string `json:"tokenTypes"` + // The token modifiers that the client supports. + TokenModifiers []string `json:"tokenModifiers"` + // The token formats the clients supports. + Formats []string `json:"formats"` + // Whether the client supports tokens that can overlap each other. + OverlappingTokenSupport bool `json:"overlappingTokenSupport,omitempty"` + // Whether the client supports tokens that can span multiple lines. + MultilineTokenSupport bool `json:"multilineTokenSupport,omitempty"` + /* + * Whether the client allows the server to actively cancel a + * semantic token request, e.g. supports returning + * LSPErrorCodes.ServerCancelled. If a server does the client + * needs to retrigger the request. + * + * @since 3.17.0 + */ + ServerCancelSupport bool `json:"serverCancelSupport,omitempty"` + /* + * Whether the client uses semantic tokens to augment existing + * syntax tokens. If set to `true` client side created syntax + * tokens and semantic tokens are both used for colorization. If + * set to `false` the client only uses the returned semantic tokens + * for colorization. + * + * If the value is `undefined` then the client behavior is not + * specified. + * + * @since 3.17.0 + */ + AugmentsSyntaxTokens bool `json:"augmentsSyntaxTokens,omitempty"` +} + +// @since 3.16.0 +type SemanticTokensDelta struct { // line 3001 + ResultID string `json:"resultId,omitempty"` + // The semantic token edits to transform a previous result into a new result. + Edits []SemanticTokensEdit `json:"edits"` +} + +// @since 3.16.0 +type SemanticTokensDeltaParams struct { // line 2968 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + /* + * The result id of a previous response. The result Id can either point to a full response + * or a delta response depending on what was received last. + */ + PreviousResultID string `json:"previousResultId"` + WorkDoneProgressParams + PartialResultParams +} + +// @since 3.16.0 +type SemanticTokensDeltaPartialResult struct { // line 3027 + Edits []SemanticTokensEdit `json:"edits"` +} + +// @since 3.16.0 +type SemanticTokensEdit struct { // line 6622 + // The start offset of the edit. + Start uint32 `json:"start"` + // The count of elements to remove. + DeleteCount uint32 `json:"deleteCount"` + // The elements to insert. + Data []uint32 `json:"data,omitempty"` +} + +// @since 3.16.0 +type SemanticTokensLegend struct { // line 9334 + // The token types a server uses. + TokenTypes []string `json:"tokenTypes"` + // The token modifiers a server uses. + TokenModifiers []string `json:"tokenModifiers"` +} + +// @since 3.16.0 +type SemanticTokensOptions struct { // line 6551 + // The legend used by the server + Legend SemanticTokensLegend `json:"legend"` + /* + * Server supports providing semantic tokens for a specific range + * of a document. + */ + Range interface{} `json:"range,omitempty"` + // Server supports providing semantic tokens for a full document. + Full bool `json:"full,omitempty"` + WorkDoneProgressOptions +} + +// @since 3.16.0 +type SemanticTokensParams struct { // line 2877 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +// @since 3.16.0 +type SemanticTokensPartialResult struct { // line 2929 + Data []uint32 `json:"data"` +} + +// @since 3.16.0 +type SemanticTokensRangeParams struct { // line 3044 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The range the semantic tokens are requested for. + Range Range `json:"range"` + WorkDoneProgressParams + PartialResultParams +} + +// @since 3.16.0 +type SemanticTokensRegistrationOptions struct { // line 2946 + TextDocumentRegistrationOptions + SemanticTokensOptions + StaticRegistrationOptions +} + +// @since 3.16.0 +type SemanticTokensWorkspaceClientCapabilities struct { // line 11003 + /* + * Whether the client implementation supports a refresh request sent from + * the server to the client. + * + * Note that this event is global and will force the client to refresh all + * semantic tokens currently shown. It should be used with absolute care + * and is useful for situation where a server for example detects a project + * wide change that requires such a calculation. + */ + RefreshSupport bool `json:"refreshSupport,omitempty"` +} + +/* + * Defines the capabilities provided by a language + * server. + */ +type ServerCapabilities struct { // line 7829 + /* + * The position encoding the server picked from the encodings offered + * by the client via the client capability `general.positionEncodings`. + * + * If the client didn't provide any position encodings the only valid + * value that a server can return is 'utf-16'. + * + * If omitted it defaults to 'utf-16'. + * + * @since 3.17.0 + */ + PositionEncoding PositionEncodingKind `json:"positionEncoding,omitempty"` + /* + * Defines how text documents are synced. Is either a detailed structure + * defining each notification or for backwards compatibility the + * TextDocumentSyncKind number. + */ + TextDocumentSync interface{} `json:"textDocumentSync,omitempty"` + /* + * Defines how notebook documents are synced. + * + * @since 3.17.0 + */ + NotebookDocumentSync interface{} `json:"notebookDocumentSync,omitempty"` + // The server provides completion support. + CompletionProvider CompletionOptions `json:"completionProvider,omitempty"` + // The server provides hover support. + HoverProvider bool `json:"hoverProvider,omitempty"` + // The server provides signature help support. + SignatureHelpProvider SignatureHelpOptions `json:"signatureHelpProvider,omitempty"` + // The server provides Goto Declaration support. + DeclarationProvider bool `json:"declarationProvider,omitempty"` + // The server provides goto definition support. + DefinitionProvider bool `json:"definitionProvider,omitempty"` + // The server provides Goto Type Definition support. + TypeDefinitionProvider interface{} `json:"typeDefinitionProvider,omitempty"` + // The server provides Goto Implementation support. + ImplementationProvider interface{} `json:"implementationProvider,omitempty"` + // The server provides find references support. + ReferencesProvider bool `json:"referencesProvider,omitempty"` + // The server provides document highlight support. + DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` + // The server provides document symbol support. + DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` + /* + * The server provides code actions. CodeActionOptions may only be + * specified if the client states that it supports + * `codeActionLiteralSupport` in its initial `initialize` request. + */ + CodeActionProvider interface{} `json:"codeActionProvider,omitempty"` + // The server provides code lens. + CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"` + // The server provides document link support. + DocumentLinkProvider DocumentLinkOptions `json:"documentLinkProvider,omitempty"` + // The server provides color provider support. + ColorProvider interface{} `json:"colorProvider,omitempty"` + // The server provides workspace symbol support. + WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` + // The server provides document formatting. + DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` + // The server provides document range formatting. + DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` + // The server provides document formatting on typing. + DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"` + /* + * The server provides rename support. RenameOptions may only be + * specified if the client states that it supports + * `prepareSupport` in its initial `initialize` request. + */ + RenameProvider interface{} `json:"renameProvider,omitempty"` + // The server provides folding provider support. + FoldingRangeProvider interface{} `json:"foldingRangeProvider,omitempty"` + // The server provides selection range support. + SelectionRangeProvider interface{} `json:"selectionRangeProvider,omitempty"` + // The server provides execute command support. + ExecuteCommandProvider ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` + /* + * The server provides call hierarchy support. + * + * @since 3.16.0 + */ + CallHierarchyProvider interface{} `json:"callHierarchyProvider,omitempty"` + /* + * The server provides linked editing range support. + * + * @since 3.16.0 + */ + LinkedEditingRangeProvider interface{} `json:"linkedEditingRangeProvider,omitempty"` + /* + * The server provides semantic tokens support. + * + * @since 3.16.0 + */ + SemanticTokensProvider interface{} `json:"semanticTokensProvider,omitempty"` + /* + * The server provides moniker support. + * + * @since 3.16.0 + */ + MonikerProvider interface{} `json:"monikerProvider,omitempty"` + /* + * The server provides type hierarchy support. + * + * @since 3.17.0 + */ + TypeHierarchyProvider interface{} `json:"typeHierarchyProvider,omitempty"` + /* + * The server provides inline values. + * + * @since 3.17.0 + */ + InlineValueProvider interface{} `json:"inlineValueProvider,omitempty"` + /* + * The server provides inlay hints. + * + * @since 3.17.0 + */ + InlayHintProvider interface{} `json:"inlayHintProvider,omitempty"` + /* + * The server has support for pull model diagnostics. + * + * @since 3.17.0 + */ + DiagnosticProvider interface{} `json:"diagnosticProvider,omitempty"` + // Workspace specific server capabilities. + Workspace Workspace6Gn `json:"workspace,omitempty"` + // Experimental server capabilities. + Experimental interface{} `json:"experimental,omitempty"` +} +type SetTraceParams struct { // line 6166 + Value TraceValues `json:"value"` +} + +/* + * Client capabilities for the showDocument request. + * + * @since 3.16.0 + */ +type ShowDocumentClientCapabilities struct { // line 12511 + /* + * The client has support for the showDocument + * request. + */ + Support bool `json:"support"` +} + +/* + * Params to show a document. + * + * @since 3.16.0 + */ +type ShowDocumentParams struct { // line 3077 + // The document uri to show. + URI URI `json:"uri"` + /* + * Indicates to show the resource in an external program. + * To show for example `https://code.visualstudio.com/` + * in the default WEB browser set `external` to `true`. + */ + External bool `json:"external,omitempty"` + /* + * An optional property to indicate whether the editor + * showing the document should take focus or not. + * Clients might ignore this property if an external + * program is started. + */ + TakeFocus bool `json:"takeFocus,omitempty"` + /* + * An optional selection range if the document is a text + * document. Clients might ignore the property if an + * external program is started or the file is not a text + * file. + */ + Selection *Range `json:"selection,omitempty"` +} + +/* + * The result of a showDocument request. + * + * @since 3.16.0 + */ +type ShowDocumentResult struct { // line 3119 + // A boolean indicating if the show was successful. + Success bool `json:"success"` +} + +// The parameters of a notification message. +type ShowMessageParams struct { // line 4205 + // The message type. See {@link MessageType} + Type MessageType `json:"type"` + // The actual message. + Message string `json:"message"` +} + +// Show message request client capabilities +type ShowMessageRequestClientCapabilities struct { // line 12484 + // Capabilities specific to the `MessageActionItem` type. + MessageActionItem *PMessageActionItemPShowMessage `json:"messageActionItem,omitempty"` +} +type ShowMessageRequestParams struct { // line 4227 + // The message type. See {@link MessageType} + Type MessageType `json:"type"` + // The actual message. + Message string `json:"message"` + // The message action items to present. + Actions []MessageActionItem `json:"actions,omitempty"` +} + +/* + * Signature help represents the signature of something + * callable. There can be multiple signature but only one + * active and only one active parameter. + */ +type SignatureHelp struct { // line 4989 + // One or more signatures. + Signatures []SignatureInformation `json:"signatures"` + /* + * The active signature. If omitted or the value lies outside the + * range of `signatures` the value defaults to zero or is ignored if + * the `SignatureHelp` has no signatures. + * + * Whenever possible implementors should make an active decision about + * the active signature and shouldn't rely on a default value. + * + * In future version of the protocol this property might become + * mandatory to better express this. + */ + ActiveSignature uint32 `json:"activeSignature,omitempty"` + /* + * The active parameter of the active signature. If omitted or the value + * lies outside the range of `signatures[activeSignature].parameters` + * defaults to 0 if the active signature has parameters. If + * the active signature has no parameters it is ignored. + * In future version of the protocol this property might become + * mandatory to better express the active parameter if the + * active signature does have any. + */ + ActiveParameter uint32 `json:"activeParameter,omitempty"` +} + +// Client Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest). +type SignatureHelpClientCapabilities struct { // line 11454 + // Whether signature help supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * The client supports the following `SignatureInformation` + * specific properties. + */ + SignatureInformation *PSignatureInformationPSignatureHelp `json:"signatureInformation,omitempty"` + /* + * The client supports to send additional context information for a + * `textDocument/signatureHelp` request. A client that opts into + * contextSupport will also support the `retriggerCharacters` on + * `SignatureHelpOptions`. + * + * @since 3.15.0 + */ + ContextSupport bool `json:"contextSupport,omitempty"` +} + +/* + * Additional information about the context in which a signature help request was triggered. + * + * @since 3.15.0 + */ +type SignatureHelpContext struct { // line 8807 + // Action that caused signature help to be triggered. + TriggerKind SignatureHelpTriggerKind `json:"triggerKind"` + /* + * Character that caused signature help to be triggered. + * + * This is undefined when `triggerKind !== SignatureHelpTriggerKind.TriggerCharacter` + */ + TriggerCharacter string `json:"triggerCharacter,omitempty"` + /* + * `true` if signature help was already showing when it was triggered. + * + * Retriggers occurs when the signature help is already active and can be caused by actions such as + * typing a trigger character, a cursor move, or document content changes. + */ + IsRetrigger bool `json:"isRetrigger"` + /* + * The currently active `SignatureHelp`. + * + * The `activeSignatureHelp` has its `SignatureHelp.activeSignature` field updated based on + * the user navigating through available signatures. + */ + ActiveSignatureHelp *SignatureHelp `json:"activeSignatureHelp,omitempty"` +} + +// Server Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest). +type SignatureHelpOptions struct { // line 8902 + // List of characters that trigger signature help automatically. + TriggerCharacters []string `json:"triggerCharacters,omitempty"` + /* + * List of characters that re-trigger signature help. + * + * These trigger characters are only active when signature help is already showing. All trigger characters + * are also counted as re-trigger characters. + * + * @since 3.15.0 + */ + RetriggerCharacters []string `json:"retriggerCharacters,omitempty"` + WorkDoneProgressOptions +} + +// Parameters for a [SignatureHelpRequest](#SignatureHelpRequest). +type SignatureHelpParams struct { // line 4961 + /* + * The signature help context. This is only available if the client specifies + * to send this using the client capability `textDocument.signatureHelp.contextSupport === true` + * + * @since 3.15.0 + */ + Context *SignatureHelpContext `json:"context,omitempty"` + TextDocumentPositionParams + WorkDoneProgressParams +} + +// Registration options for a [SignatureHelpRequest](#SignatureHelpRequest). +type SignatureHelpRegistrationOptions struct { // line 5024 + TextDocumentRegistrationOptions + SignatureHelpOptions +} +type SignatureHelpTriggerKind uint32 // line 13606 +/* + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ +type SignatureInformation struct { // line 8848 + /* + * The label of this signature. Will be shown in + * the UI. + */ + Label string `json:"label"` + /* + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + Documentation string `json:"documentation,omitempty"` + // The parameters of this signature. + Parameters []ParameterInformation `json:"parameters,omitempty"` + /* + * The index of the active parameter. + * + * If provided, this is used in place of `SignatureHelp.activeParameter`. + * + * @since 3.16.0 + */ + ActiveParameter uint32 `json:"activeParameter,omitempty"` +} + +/* + * Static registration options to be returned in the initialize + * request. + */ +type StaticRegistrationOptions struct { // line 6348 + /* + * The id used to register the request. The id can be used to deregister + * the request again. See also Registration#id. + */ + ID string `json:"id,omitempty"` +} + +/* + * Represents information about programming constructs like variables, classes, + * interfaces etc. + */ +type SymbolInformation struct { // line 5202 + /* + * Indicates if this symbol is deprecated. + * + * @deprecated Use tags instead + */ + Deprecated bool `json:"deprecated,omitempty"` + /* + * The location of this symbol. The location's range is used by a tool + * to reveal the location in the editor. If the symbol is selected in the + * tool the range's start information is used to position the cursor. So + * the range usually spans more than the actual symbol's name and does + * normally include things like visibility modifiers. + * + * The range doesn't have to denote a node range in the sense of an abstract + * syntax tree. It can therefore not be used to re-construct a hierarchy of + * the symbols. + */ + Location Location `json:"location"` + // The name of this symbol. + Name string `json:"name"` + // The kind of this symbol. + Kind SymbolKind `json:"kind"` + /* + * Tags for this symbol. + * + * @since 3.16.0 + */ + Tags []SymbolTag `json:"tags,omitempty"` + /* + * The name of the symbol containing this symbol. This information is for + * user interface purposes (e.g. to render a qualifier in the user interface + * if necessary). It can't be used to re-infer a hierarchy for the document + * symbols. + */ + ContainerName string `json:"containerName,omitempty"` +} +type SymbolKind uint32 // line 12867 +type SymbolTag uint32 // line 12981 +// Describe options to be used when registered for text document change events. +type TextDocumentChangeRegistrationOptions struct { // line 4334 + // How documents are synced to the server. + SyncKind TextDocumentSyncKind `json:"syncKind"` + TextDocumentRegistrationOptions +} + +// Text document specific client capabilities. +type TextDocumentClientCapabilities struct { // line 10349 + // Defines which synchronization capabilities the client supports. + Synchronization *TextDocumentSyncClientCapabilities `json:"synchronization,omitempty"` + // Capabilities specific to the `textDocument/completion` request. + Completion CompletionClientCapabilities `json:"completion,omitempty"` + // Capabilities specific to the `textDocument/hover` request. + Hover HoverClientCapabilities `json:"hover,omitempty"` + // Capabilities specific to the `textDocument/signatureHelp` request. + SignatureHelp *SignatureHelpClientCapabilities `json:"signatureHelp,omitempty"` + /* + * Capabilities specific to the `textDocument/declaration` request. + * + * @since 3.14.0 + */ + Declaration *DeclarationClientCapabilities `json:"declaration,omitempty"` + // Capabilities specific to the `textDocument/definition` request. + Definition *DefinitionClientCapabilities `json:"definition,omitempty"` + /* + * Capabilities specific to the `textDocument/typeDefinition` request. + * + * @since 3.6.0 + */ + TypeDefinition *TypeDefinitionClientCapabilities `json:"typeDefinition,omitempty"` + /* + * Capabilities specific to the `textDocument/implementation` request. + * + * @since 3.6.0 + */ + Implementation *ImplementationClientCapabilities `json:"implementation,omitempty"` + // Capabilities specific to the `textDocument/references` request. + References *ReferenceClientCapabilities `json:"references,omitempty"` + // Capabilities specific to the `textDocument/documentHighlight` request. + DocumentHighlight *DocumentHighlightClientCapabilities `json:"documentHighlight,omitempty"` + // Capabilities specific to the `textDocument/documentSymbol` request. + DocumentSymbol DocumentSymbolClientCapabilities `json:"documentSymbol,omitempty"` + // Capabilities specific to the `textDocument/codeAction` request. + CodeAction CodeActionClientCapabilities `json:"codeAction,omitempty"` + // Capabilities specific to the `textDocument/codeLens` request. + CodeLens *CodeLensClientCapabilities `json:"codeLens,omitempty"` + // Capabilities specific to the `textDocument/documentLink` request. + DocumentLink *DocumentLinkClientCapabilities `json:"documentLink,omitempty"` + /* + * Capabilities specific to the `textDocument/documentColor` and the + * `textDocument/colorPresentation` request. + * + * @since 3.6.0 + */ + ColorProvider *DocumentColorClientCapabilities `json:"colorProvider,omitempty"` + // Capabilities specific to the `textDocument/formatting` request. + Formatting *DocumentFormattingClientCapabilities `json:"formatting,omitempty"` + // Capabilities specific to the `textDocument/rangeFormatting` request. + RangeFormatting *DocumentRangeFormattingClientCapabilities `json:"rangeFormatting,omitempty"` + // Capabilities specific to the `textDocument/onTypeFormatting` request. + OnTypeFormatting *DocumentOnTypeFormattingClientCapabilities `json:"onTypeFormatting,omitempty"` + // Capabilities specific to the `textDocument/rename` request. + Rename RenameClientCapabilities `json:"rename,omitempty"` + /* + * Capabilities specific to the `textDocument/foldingRange` request. + * + * @since 3.10.0 + */ + FoldingRange FoldingRangeClientCapabilities `json:"foldingRange,omitempty"` + /* + * Capabilities specific to the `textDocument/selectionRange` request. + * + * @since 3.15.0 + */ + SelectionRange *SelectionRangeClientCapabilities `json:"selectionRange,omitempty"` + // Capabilities specific to the `textDocument/publishDiagnostics` notification. + PublishDiagnostics PublishDiagnosticsClientCapabilities `json:"publishDiagnostics,omitempty"` + /* + * Capabilities specific to the various call hierarchy requests. + * + * @since 3.16.0 + */ + CallHierarchy *CallHierarchyClientCapabilities `json:"callHierarchy,omitempty"` + /* + * Capabilities specific to the various semantic token request. + * + * @since 3.16.0 + */ + SemanticTokens SemanticTokensClientCapabilities `json:"semanticTokens,omitempty"` + /* + * Capabilities specific to the `textDocument/linkedEditingRange` request. + * + * @since 3.16.0 + */ + LinkedEditingRange *LinkedEditingRangeClientCapabilities `json:"linkedEditingRange,omitempty"` + /* + * Client capabilities specific to the `textDocument/moniker` request. + * + * @since 3.16.0 + */ + Moniker *MonikerClientCapabilities `json:"moniker,omitempty"` + /* + * Capabilities specific to the various type hierarchy requests. + * + * @since 3.17.0 + */ + TypeHierarchy *TypeHierarchyClientCapabilities `json:"typeHierarchy,omitempty"` + /* + * Capabilities specific to the `textDocument/inlineValue` request. + * + * @since 3.17.0 + */ + InlineValue *InlineValueClientCapabilities `json:"inlineValue,omitempty"` + /* + * Capabilities specific to the `textDocument/inlayHint` request. + * + * @since 3.17.0 + */ + InlayHint *InlayHintClientCapabilities `json:"inlayHint,omitempty"` + /* + * Capabilities specific to the diagnostic pull model. + * + * @since 3.17.0 + */ + Diagnostic *DiagnosticClientCapabilities `json:"diagnostic,omitempty"` +} + +/* + * An event describing a change to a text document. If only a text is provided + * it is considered to be the full content of the document. + */ +type TextDocumentContentChangeEvent = Msg_TextDocumentContentChangeEvent // (alias) line 14028 +/* + * Describes textual changes on a text document. A TextDocumentEdit describes all changes + * on a document version Si and after they are applied move the document to version Si+1. + * So the creator of a TextDocumentEdit doesn't need to sort the array of edits or do any + * kind of ordering. However the edits must be non overlapping. + */ +type TextDocumentEdit struct { // line 6682 + // The text document to change. + TextDocument OptionalVersionedTextDocumentIdentifier `json:"textDocument"` + /* + * The edits to be applied. + * + * @since 3.16.0 - support for AnnotatedTextEdit. This is guarded using a + * client capability. + */ + Edits []TextEdit `json:"edits"` +} + +/* + * A document filter denotes a document by different properties like + * the [language](#TextDocument.languageId), the [scheme](#Uri.scheme) of + * its resource, or a glob-pattern that is applied to the [path](#TextDocument.fileName). + * + * Glob patterns can have the following syntax: + * - `*` to match one or more characters in a path segment + * - `?` to match on one character in a path segment + * - `**` to match any number of path segments, including none + * - `{}` to group sub patterns into an OR expression. (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + * + * @sample A language filter that applies to typescript files on disk: `{ language: 'typescript', scheme: 'file' }` + * @sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` + * + * @since 3.17.0 + */ +type TextDocumentFilter = Msg_TextDocumentFilter // (alias) line 14154 +// A literal to identify a text document in the client. +type TextDocumentIdentifier struct { // line 6424 + // The text document's uri. + URI DocumentURI `json:"uri"` +} + +/* + * An item to transfer a text document from the client to the + * server. + */ +type TextDocumentItem struct { // line 7410 + // The text document's uri. + URI DocumentURI `json:"uri"` + // The text document's language identifier. + LanguageID string `json:"languageId"` + /* + * The version number of this document (it will increase after each + * change, including undo/redo). + */ + Version int32 `json:"version"` + // The content of the opened text document. + Text string `json:"text"` +} + +/* + * A parameter literal used in requests to pass a text document and a position inside that + * document. + */ +type TextDocumentPositionParams struct { // line 6241 + // The text document. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The position inside the text document. + Position Position `json:"position"` +} + +// General text document registration options. +type TextDocumentRegistrationOptions struct { // line 2390 + /* + * A document selector to identify the scope of the registration. If set to null + * the document selector provided on the client side will be used. + */ + DocumentSelector DocumentSelector `json:"documentSelector"` +} +type TextDocumentSaveReason uint32 // line 13135 +// Save registration options. +type TextDocumentSaveRegistrationOptions struct { // line 4391 + TextDocumentRegistrationOptions + SaveOptions +} +type TextDocumentSyncClientCapabilities struct { // line 11153 + // Whether text document synchronization supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + // The client supports sending will save notifications. + WillSave bool `json:"willSave,omitempty"` + /* + * The client supports sending a will save request and + * waits for a response providing text edits which will + * be applied to the document before it is saved. + */ + WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` + // The client supports did save notifications. + DidSave bool `json:"didSave,omitempty"` +} +type TextDocumentSyncKind uint32 // line 13110 +type TextDocumentSyncOptions struct { // line 9762 + /* + * Open and close notifications are sent to the server. If omitted open close notification should not + * be sent. + */ + OpenClose bool `json:"openClose,omitempty"` + /* + * Change notifications are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full + * and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSyncKind.None. + */ + Change TextDocumentSyncKind `json:"change,omitempty"` + /* + * If present will save notifications are sent to the server. If omitted the notification should not be + * sent. + */ + WillSave bool `json:"willSave,omitempty"` + /* + * If present will save wait until requests are sent to the server. If omitted the request should not be + * sent. + */ + WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` + /* + * If present save notifications are sent to the server. If omitted the notification should not be + * sent. + */ + Save SaveOptions `json:"save,omitempty"` +} + +// A text edit applicable to a text document. +type TextEdit struct { // line 4428 + /* + * The range of the text document to be manipulated. To insert + * text into a document create a range where start === end. + */ + Range Range `json:"range"` + /* + * The string to be inserted. For delete operations use an + * empty string. + */ + NewText string `json:"newText"` +} +type TokenFormat string // line 13762 +type TraceValues string // line 13409 +// Since 3.6.0 +type TypeDefinitionClientCapabilities struct { // line 11585 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `TypeDefinitionRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + /* + * The client supports additional metadata in the form of definition links. + * + * Since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} +type TypeDefinitionOptions struct { // line 6363 + WorkDoneProgressOptions +} +type TypeDefinitionParams struct { // line 2131 + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} +type TypeDefinitionRegistrationOptions struct { // line 2151 + TextDocumentRegistrationOptions + TypeDefinitionOptions + StaticRegistrationOptions +} + +// @since 3.17.0 +type TypeHierarchyClientCapabilities struct { // line 12363 + /* + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// @since 3.17.0 +type TypeHierarchyItem struct { // line 3432 + // The name of this item. + Name string `json:"name"` + // The kind of this item. + Kind SymbolKind `json:"kind"` + // Tags for this item. + Tags []SymbolTag `json:"tags,omitempty"` + // More detail for this item, e.g. the signature of a function. + Detail string `json:"detail,omitempty"` + // The resource identifier of this item. + URI DocumentURI `json:"uri"` + /* + * The range enclosing this symbol not including leading/trailing whitespace + * but everything else, e.g. comments and code. + */ + Range Range `json:"range"` + /* + * The range that should be selected and revealed when this symbol is being + * picked, e.g. the name of a function. Must be contained by the + * [`range`](#TypeHierarchyItem.range). + */ + SelectionRange Range `json:"selectionRange"` + /* + * A data entry field that is preserved between a type hierarchy prepare and + * supertypes or subtypes requests. It could also be used to identify the + * type hierarchy in the server, helping improve the performance on + * resolving supertypes and subtypes. + */ + Data interface{} `json:"data,omitempty"` +} + +/* + * Type hierarchy options used during static registration. + * + * @since 3.17.0 + */ +type TypeHierarchyOptions struct { // line 6941 + WorkDoneProgressOptions +} + +/* + * The parameter of a `textDocument/prepareTypeHierarchy` request. + * + * @since 3.17.0 + */ +type TypeHierarchyPrepareParams struct { // line 3414 + TextDocumentPositionParams + WorkDoneProgressParams +} + +/* + * Type hierarchy options used during static or dynamic registration. + * + * @since 3.17.0 + */ +type TypeHierarchyRegistrationOptions struct { // line 3509 + TextDocumentRegistrationOptions + TypeHierarchyOptions + StaticRegistrationOptions +} + +/* + * The parameter of a `typeHierarchy/subtypes` request. + * + * @since 3.17.0 + */ +type TypeHierarchySubtypesParams struct { // line 3555 + Item TypeHierarchyItem `json:"item"` + WorkDoneProgressParams + PartialResultParams +} + +/* + * The parameter of a `typeHierarchy/supertypes` request. + * + * @since 3.17.0 + */ +type TypeHierarchySupertypesParams struct { // line 3531 + Item TypeHierarchyItem `json:"item"` + WorkDoneProgressParams + PartialResultParams +} + +// created for Tuple +type UIntCommaUInt struct { // line 10101 + Fld0 uint32 `json:"fld0"` + Fld1 uint32 `json:"fld1"` +} +type URI = string // (alias) line 0 +/* + * A diagnostic report indicating that the last returned + * report is still accurate. + * + * @since 3.17.0 + */ +type UnchangedDocumentDiagnosticReport struct { // line 7275 + /* + * A document diagnostic report indicating + * no changes to the last result. A server can + * only return `unchanged` if result ids are + * provided. + */ + Kind string `json:"kind"` + /* + * A result id which will be sent on the next + * diagnostic request for the same document. + */ + ResultID string `json:"resultId"` +} +type UniquenessLevel string // line 12997 +// General parameters to unregister a request or notification. +type Unregistration struct { // line 7633 + /* + * The id used to unregister the request or notification. Usually an id + * provided during the register request. + */ + ID string `json:"id"` + // The method to unregister for. + Method string `json:"method"` +} +type UnregistrationParams struct { // line 4075 + Unregisterations []Unregistration `json:"unregisterations"` +} + +/* + * A versioned notebook document identifier. + * + * @since 3.17.0 + */ +type VersionedNotebookDocumentIdentifier struct { // line 7448 + // The version number of this notebook document. + Version int32 `json:"version"` + // The notebook document's uri. + URI URI `json:"uri"` +} + +// A text document identifier to denote a specific version of a text document. +type VersionedTextDocumentIdentifier struct { // line 8465 + // The version number of this document. + Version int32 `json:"version"` + TextDocumentIdentifier +} +type WatchKind = uint32 // line 13505 +// The parameters sent in a will save text document notification. +type WillSaveTextDocumentParams struct { // line 4406 + // The document that will be saved. + TextDocument TextDocumentIdentifier `json:"textDocument"` + // The 'TextDocumentSaveReason'. + Reason TextDocumentSaveReason `json:"reason"` +} +type WindowClientCapabilities struct { // line 10655 + /* + * It indicates whether the client supports server initiated + * progress using the `window/workDoneProgress/create` request. + * + * The capability also controls Whether client supports handling + * of progress notifications. If set servers are allowed to report a + * `workDoneProgress` property in the request specific server + * capabilities. + * + * @since 3.15.0 + */ + WorkDoneProgress bool `json:"workDoneProgress,omitempty"` + /* + * Capabilities specific to the showMessage request. + * + * @since 3.16.0 + */ + ShowMessage *ShowMessageRequestClientCapabilities `json:"showMessage,omitempty"` + /* + * Capabilities specific to the showDocument request. + * + * @since 3.16.0 + */ + ShowDocument *ShowDocumentClientCapabilities `json:"showDocument,omitempty"` +} +type WorkDoneProgressBegin struct { // line 6059 + Kind string `json:"kind"` + /* + * Mandatory title of the progress operation. Used to briefly inform about + * the kind of operation being performed. + * + * Examples: \"Indexing\" or \"Linking dependencies\". + */ + Title string `json:"title"` + /* + * Controls if a cancel button should show to allow the user to cancel the + * long running operation. Clients that don't support cancellation are allowed + * to ignore the setting. + */ + Cancellable bool `json:"cancellable,omitempty"` + /* + * Optional, more detailed associated progress message. Contains + * complementary information to the `title`. + * + * Examples: \"3/25 files\", \"project/src/module2\", \"node_modules/some_dep\". + * If unset, the previous progress message (if any) is still valid. + */ + Message string `json:"message,omitempty"` + /* + * Optional progress percentage to display (value 100 is considered 100%). + * If not provided infinite progress is assumed and clients are allowed + * to ignore the `percentage` value in subsequent in report notifications. + * + * The value should be steadily rising. Clients are free to ignore values + * that are not following this rule. The value range is [0, 100]. + */ + Percentage uint32 `json:"percentage,omitempty"` +} +type WorkDoneProgressCancelParams struct { // line 2647 + // The token to be used to report progress. + Token ProgressToken `json:"token"` +} +type WorkDoneProgressCreateParams struct { // line 2634 + // The token to be used to report progress. + Token ProgressToken `json:"token"` +} +type WorkDoneProgressEnd struct { // line 6145 + Kind string `json:"kind"` + /* + * Optional, a final message indicating to for example indicate the outcome + * of the operation. + */ + Message string `json:"message,omitempty"` +} +type WorkDoneProgressOptions struct { // line 2377 + WorkDoneProgress bool `json:"workDoneProgress,omitempty"` +} + +// created for And +type WorkDoneProgressOptionsAndTextDocumentRegistrationOptions struct { // line 204 + WorkDoneProgressOptions + TextDocumentRegistrationOptions +} +type WorkDoneProgressParams struct { // line 6263 + // An optional token that a server can use to report work done progress. + WorkDoneToken ProgressToken `json:"workDoneToken,omitempty"` +} +type WorkDoneProgressReport struct { // line 6106 + Kind string `json:"kind"` + /* + * Controls enablement state of a cancel button. + * + * Clients that don't support cancellation or don't support controlling the button's + * enablement state are allowed to ignore the property. + */ + Cancellable bool `json:"cancellable,omitempty"` + /* + * Optional, more detailed associated progress message. Contains + * complementary information to the `title`. + * + * Examples: \"3/25 files\", \"project/src/module2\", \"node_modules/some_dep\". + * If unset, the previous progress message (if any) is still valid. + */ + Message string `json:"message,omitempty"` + /* + * Optional progress percentage to display (value 100 is considered 100%). + * If not provided infinite progress is assumed and clients are allowed + * to ignore the `percentage` value in subsequent in report notifications. + * + * The value should be steadily rising. Clients are free to ignore values + * that are not following this rule. The value range is [0, 100] + */ + Percentage uint32 `json:"percentage,omitempty"` +} + +// created for Literal +type Workspace6Gn struct { // line 8424 + /* + * The server supports workspace folder. + * + * @since 3.6.0 + */ + WorkspaceFolders WorkspaceFolders5Gn `json:"workspaceFolders"` + /* + * The server is interested in notifications/requests for operations on files. + * + * @since 3.16.0 + */ + FileOperations FileOperationOptions `json:"fileOperations"` +} + +// Workspace specific client capabilities. +type WorkspaceClientCapabilities struct { // line 10210 + /* + * The client supports applying batch edits + * to the workspace by supporting the request + * 'workspace/applyEdit' + */ + ApplyEdit bool `json:"applyEdit,omitempty"` + // Capabilities specific to `WorkspaceEdit`s. + WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"` + // Capabilities specific to the `workspace/didChangeConfiguration` notification. + DidChangeConfiguration DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"` + // Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + DidChangeWatchedFiles DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"` + // Capabilities specific to the `workspace/symbol` request. + Symbol *WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"` + // Capabilities specific to the `workspace/executeCommand` request. + ExecuteCommand *ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"` + /* + * The client has support for workspace folders. + * + * @since 3.6.0 + */ + WorkspaceFolders bool `json:"workspaceFolders,omitempty"` + /* + * The client supports `workspace/configuration` requests. + * + * @since 3.6.0 + */ + Configuration bool `json:"configuration,omitempty"` + /* + * Capabilities specific to the semantic token requests scoped to the + * workspace. + * + * @since 3.16.0. + */ + SemanticTokens *SemanticTokensWorkspaceClientCapabilities `json:"semanticTokens,omitempty"` + /* + * Capabilities specific to the code lens requests scoped to the + * workspace. + * + * @since 3.16.0. + */ + CodeLens *CodeLensWorkspaceClientCapabilities `json:"codeLens,omitempty"` + /* + * The client has support for file notifications/requests for user operations on files. + * + * Since 3.16.0 + */ + FileOperations *FileOperationClientCapabilities `json:"fileOperations,omitempty"` + /* + * Capabilities specific to the inline values requests scoped to the + * workspace. + * + * @since 3.17.0. + */ + InlineValue *InlineValueWorkspaceClientCapabilities `json:"inlineValue,omitempty"` + /* + * Capabilities specific to the inlay hint requests scoped to the + * workspace. + * + * @since 3.17.0. + */ + InlayHint *InlayHintWorkspaceClientCapabilities `json:"inlayHint,omitempty"` + /* + * Capabilities specific to the diagnostic requests scoped to the + * workspace. + * + * @since 3.17.0. + */ + Diagnostics *DiagnosticWorkspaceClientCapabilities `json:"diagnostics,omitempty"` +} + +/* + * Parameters of the workspace diagnostic request. + * + * @since 3.17.0 + */ +type WorkspaceDiagnosticParams struct { // line 3899 + // The additional identifier provided during registration. + Identifier string `json:"identifier,omitempty"` + /* + * The currently known diagnostic reports with their + * previous result ids. + */ + PreviousResultIds []PreviousResultID `json:"previousResultIds"` + WorkDoneProgressParams + PartialResultParams +} + +/* + * A workspace diagnostic report. + * + * @since 3.17.0 + */ +type WorkspaceDiagnosticReport struct { // line 3936 + Items []WorkspaceDocumentDiagnosticReport `json:"items"` +} + +/* + * A partial result for a workspace diagnostic report. + * + * @since 3.17.0 + */ +type WorkspaceDiagnosticReportPartialResult struct { // line 3953 + Items []WorkspaceDocumentDiagnosticReport `json:"items"` +} + +/* + * A workspace diagnostic document report. + * + * @since 3.17.0 + */ +type WorkspaceDocumentDiagnosticReport = Or_WorkspaceDocumentDiagnosticReport // (alias) line 14010 +/* + * A workspace edit represents changes to many resources managed in the workspace. The edit + * should either provide `changes` or `documentChanges`. If documentChanges are present + * they are preferred over `changes` if the client can handle versioned document edits. + * + * Since version 3.13.0 a workspace edit can contain resource operations as well. If resource + * operations are present clients need to execute the operations in the order in which they + * are provided. So a workspace edit for example can consist of the following two changes: + * (1) a create file a.txt and (2) a text document edit which insert text into file a.txt. + * + * An invalid sequence (e.g. (1) delete file a.txt and (2) insert text into file a.txt) will + * cause failure of the operation. How the client recovers from the failure is described by + * the client capability: `workspace.workspaceEdit.failureHandling` + */ +type WorkspaceEdit struct { // line 3215 + // Holds changes to existing resources. + Changes map[DocumentURI][]TextEdit `json:"changes,omitempty"` + /* + * Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes + * are either an array of `TextDocumentEdit`s to express changes to n different text documents + * where each text document edit addresses a specific version of a text document. Or it can contain + * above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations. + * + * Whether a client supports versioned document edits is expressed via + * `workspace.workspaceEdit.documentChanges` client capability. + * + * If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then + * only plain `TextEdit`s using the `changes` property are supported. + */ + DocumentChanges []DocumentChanges `json:"documentChanges,omitempty"` + /* + * A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and + * delete file / folder operations. + * + * Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`. + * + * @since 3.16.0 + */ + ChangeAnnotations map[ChangeAnnotationIdentifier]ChangeAnnotation `json:"changeAnnotations,omitempty"` +} +type WorkspaceEditClientCapabilities struct { // line 10794 + // The client supports versioned document changes in `WorkspaceEdit`s + DocumentChanges bool `json:"documentChanges,omitempty"` + /* + * The resource operations the client supports. Clients should at least + * support 'create', 'rename' and 'delete' files and folders. + * + * @since 3.13.0 + */ + ResourceOperations []ResourceOperationKind `json:"resourceOperations,omitempty"` + /* + * The failure handling strategy of a client if applying the workspace edit + * fails. + * + * @since 3.13.0 + */ + FailureHandling FailureHandlingKind `json:"failureHandling,omitempty"` + /* + * Whether the client normalizes line endings to the client specific + * setting. + * If set to `true` the client will normalize line ending characters + * in a workspace edit to the client-specified new line + * character. + * + * @since 3.16.0 + */ + NormalizesLineEndings bool `json:"normalizesLineEndings,omitempty"` + /* + * Whether the client in general supports change annotations on text edits, + * create file, rename file and delete file changes. + * + * @since 3.16.0 + */ + ChangeAnnotationSupport *PChangeAnnotationSupportPWorkspaceEdit `json:"changeAnnotationSupport,omitempty"` +} + +// A workspace folder inside a client. +type WorkspaceFolder struct { // line 2171 + // The associated URI for this workspace folder. + URI URI `json:"uri"` + /* + * The name of the workspace folder. Used to refer to this + * workspace folder in the user interface. + */ + Name string `json:"name"` +} +type WorkspaceFolders5Gn struct { // line 9959 + // The server has support for workspace folders + Supported bool `json:"supported,omitempty"` + /* + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a string is provided the string is treated as an ID + * under which the notification is registered on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + */ + ChangeNotifications string `json:"changeNotifications,omitempty"` +} + +// The workspace folder change event. +type WorkspaceFoldersChangeEvent struct { // line 6373 + // The array of added workspace folders + Added []WorkspaceFolder `json:"added"` + // The array of the removed workspace folders + Removed []WorkspaceFolder `json:"removed"` +} +type WorkspaceFoldersInitializeParams struct { // line 7802 + /* + * The workspace folders configured in the client when the server starts. + * + * This property is only available if the client supports workspace folders. + * It can be `null` if the client supports workspace folders but none are + * configured. + * + * @since 3.6.0 + */ + WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders,omitempty"` +} +type WorkspaceFoldersServerCapabilities struct { // line 9959 + // The server has support for workspace folders + Supported bool `json:"supported,omitempty"` + /* + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a string is provided the string is treated as an ID + * under which the notification is registered on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + */ + ChangeNotifications string `json:"changeNotifications,omitempty"` +} + +/* + * A full document diagnostic report for a workspace diagnostic result. + * + * @since 3.17.0 + */ +type WorkspaceFullDocumentDiagnosticReport struct { // line 9542 + // The URI for which diagnostic information is reported. + URI DocumentURI `json:"uri"` + /* + * The version number for which the diagnostics are reported. + * If the document is not marked as open `null` can be provided. + */ + Version int32 `json:"version"` + FullDocumentDiagnosticReport +} + +/* + * A special workspace symbol that supports locations without a range. + * + * See also SymbolInformation. + * + * @since 3.17.0 + */ +type WorkspaceSymbol struct { // line 5534 + /* + * The location of the symbol. Whether a server is allowed to + * return a location without a range depends on the client + * capability `workspace.symbol.resolveSupport`. + * + * See SymbolInformation#location for more details. + */ + Location OrPLocation_workspace_symbol `json:"location"` + /* + * A data entry field that is preserved on a workspace symbol between a + * workspace symbol request and a workspace symbol resolve request. + */ + Data interface{} `json:"data,omitempty"` + BaseSymbolInformation +} + +// Client capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). +type WorkspaceSymbolClientCapabilities struct { // line 10901 + // Symbol request supports dynamic registration. + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + // Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. + SymbolKind *PSymbolKindPSymbol `json:"symbolKind,omitempty"` + /* + * The client supports tags on `SymbolInformation`. + * Clients supporting tags have to handle unknown tags gracefully. + * + * @since 3.16.0 + */ + TagSupport *PTagSupportPSymbol `json:"tagSupport,omitempty"` + /* + * The client support partial workspace symbols. The client will send the + * request `workspaceSymbol/resolve` to the server to resolve additional + * properties. + * + * @since 3.17.0 + */ + ResolveSupport *PResolveSupportPSymbol `json:"resolveSupport,omitempty"` +} + +// Server capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). +type WorkspaceSymbolOptions struct { // line 9125 + /* + * The server provides support to resolve additional + * information for a workspace symbol. + * + * @since 3.17.0 + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +// The parameters of a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). +type WorkspaceSymbolParams struct { // line 5510 + /* + * A query string to filter symbols by. Clients may send an empty + * string here to request all symbols. + */ + Query string `json:"query"` + WorkDoneProgressParams + PartialResultParams +} + +// Registration options for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). +type WorkspaceSymbolRegistrationOptions struct { // line 5583 + WorkspaceSymbolOptions +} + +/* + * An unchanged document diagnostic report for a workspace diagnostic result. + * + * @since 3.17.0 + */ +type WorkspaceUnchangedDocumentDiagnosticReport struct { // line 9580 + // The URI for which diagnostic information is reported. + URI DocumentURI `json:"uri"` + /* + * The version number for which the diagnostics are reported. + * If the document is not marked as open `null` can be provided. + */ + Version int32 `json:"version"` + UnchangedDocumentDiagnosticReport +} + +// The initialize parameters +type XInitializeParams struct { // line 7655 + /* + * The process Id of the parent process that started + * the server. + * + * Is `null` if the process has not been started by another process. + * If the parent process is not alive then the server should exit. + */ + ProcessID int32 `json:"processId"` + /* + * Information about the client + * + * @since 3.15.0 + */ + ClientInfo Msg_XInitializeParams_clientInfo `json:"clientInfo,omitempty"` + /* + * The locale the client is currently showing the user interface + * in. This must not necessarily be the locale of the operating + * system. + * + * Uses IETF language tags as the value's syntax + * (See https://en.wikipedia.org/wiki/IETF_language_tag) + * + * @since 3.16.0 + */ + Locale string `json:"locale,omitempty"` + /* + * The rootPath of the workspace. Is null + * if no folder is open. + * + * @deprecated in favour of rootUri. + */ + RootPath string `json:"rootPath,omitempty"` + /* + * The rootUri of the workspace. Is null if no + * folder is open. If both `rootPath` and `rootUri` are set + * `rootUri` wins. + * + * @deprecated in favour of workspaceFolders. + */ + RootURI DocumentURI `json:"rootUri"` + // The capabilities provided by the client (editor or tool) + Capabilities ClientCapabilities `json:"capabilities"` + // User provided initialization options. + InitializationOptions interface{} `json:"initializationOptions,omitempty"` + // The initial trace setting. If omitted trace is disabled ('off'). + Trace string `json:"trace,omitempty"` +} + +// The initialize parameters +type _InitializeParams struct { // line 7655 + /* + * The process Id of the parent process that started + * the server. + * + * Is `null` if the process has not been started by another process. + * If the parent process is not alive then the server should exit. + */ + ProcessID int32 `json:"processId"` + /* + * Information about the client + * + * @since 3.15.0 + */ + ClientInfo *Msg_XInitializeParams_clientInfo `json:"clientInfo,omitempty"` + /* + * The locale the client is currently showing the user interface + * in. This must not necessarily be the locale of the operating + * system. + * + * Uses IETF language tags as the value's syntax + * (See https://en.wikipedia.org/wiki/IETF_language_tag) + * + * @since 3.16.0 + */ + Locale string `json:"locale,omitempty"` + /* + * The rootPath of the workspace. Is null + * if no folder is open. + * + * @deprecated in favour of rootUri. + */ + RootPath string `json:"rootPath,omitempty"` + /* + * The rootUri of the workspace. Is null if no + * folder is open. If both `rootPath` and `rootUri` are set + * `rootUri` wins. + * + * @deprecated in favour of workspaceFolders. + */ + RootURI DocumentURI `json:"rootUri"` + // The capabilities provided by the client (editor or tool) + Capabilities ClientCapabilities `json:"capabilities"` + // User provided initialization options. + InitializationOptions interface{} `json:"initializationOptions,omitempty"` + // The initial trace setting. If omitted trace is disabled ('off'). + Trace string `json:"trace,omitempty"` +} + +const ( + // A set of predefined code action kinds + // Empty kind. + Empty CodeActionKind = "" // line 13359 + // Base kind for quickfix actions: 'quickfix' + QuickFix CodeActionKind = "quickfix" // line 13364 + // Base kind for refactoring actions: 'refactor' + Refactor CodeActionKind = "refactor" // line 13369 + /* + * Base kind for refactoring extraction actions: 'refactor.extract' + * + * Example extract actions: + * + * - Extract method + * - Extract function + * - Extract variable + * - Extract interface from class + * - ... + */ + RefactorExtract CodeActionKind = "refactor.extract" // line 13374 + /* + * Base kind for refactoring inline actions: 'refactor.inline' + * + * Example inline actions: + * + * - Inline function + * - Inline variable + * - Inline constant + * - ... + */ + RefactorInline CodeActionKind = "refactor.inline" // line 13379 + /* + * Base kind for refactoring rewrite actions: 'refactor.rewrite' + * + * Example rewrite actions: + * + * - Convert JavaScript function to class + * - Add or remove parameter + * - Encapsulate field + * - Make method static + * - Move method to base class + * - ... + */ + RefactorRewrite CodeActionKind = "refactor.rewrite" // line 13384 + /* + * Base kind for source actions: `source` + * + * Source code actions apply to the entire file. + */ + Source CodeActionKind = "source" // line 13389 + // Base kind for an organize imports source action: `source.organizeImports` + SourceOrganizeImports CodeActionKind = "source.organizeImports" // line 13394 + /* + * Base kind for auto-fix source actions: `source.fixAll`. + * + * Fix all actions automatically fix errors that have a clear fix that do not require user input. + * They should not suppress errors or perform unsafe fixes such as generating new types or classes. + * + * @since 3.15.0 + */ + SourceFixAll CodeActionKind = "source.fixAll" // line 13399 + /* + * The reason why code actions were requested. + * + * @since 3.17.0 + */ + // Code actions were explicitly requested by the user or by an extension. + CodeActionInvoked CodeActionTriggerKind = 1 // line 13639 + /* + * Code actions were requested automatically. + * + * This typically happens when current selection in a file changes, but can + * also be triggered when file content changes. + */ + CodeActionAutomatic CodeActionTriggerKind = 2 // line 13644 + // The kind of a completion entry. + TextCompletion CompletionItemKind = 1 // line 13167 + MethodCompletion CompletionItemKind = 2 // line 13171 + FunctionCompletion CompletionItemKind = 3 // line 13175 + ConstructorCompletion CompletionItemKind = 4 // line 13179 + FieldCompletion CompletionItemKind = 5 // line 13183 + VariableCompletion CompletionItemKind = 6 // line 13187 + ClassCompletion CompletionItemKind = 7 // line 13191 + InterfaceCompletion CompletionItemKind = 8 // line 13195 + ModuleCompletion CompletionItemKind = 9 // line 13199 + PropertyCompletion CompletionItemKind = 10 // line 13203 + UnitCompletion CompletionItemKind = 11 // line 13207 + ValueCompletion CompletionItemKind = 12 // line 13211 + EnumCompletion CompletionItemKind = 13 // line 13215 + KeywordCompletion CompletionItemKind = 14 // line 13219 + SnippetCompletion CompletionItemKind = 15 // line 13223 + ColorCompletion CompletionItemKind = 16 // line 13227 + FileCompletion CompletionItemKind = 17 // line 13231 + ReferenceCompletion CompletionItemKind = 18 // line 13235 + FolderCompletion CompletionItemKind = 19 // line 13239 + EnumMemberCompletion CompletionItemKind = 20 // line 13243 + ConstantCompletion CompletionItemKind = 21 // line 13247 + StructCompletion CompletionItemKind = 22 // line 13251 + EventCompletion CompletionItemKind = 23 // line 13255 + OperatorCompletion CompletionItemKind = 24 // line 13259 + TypeParameterCompletion CompletionItemKind = 25 // line 13263 + /* + * Completion item tags are extra annotations that tweak the rendering of a completion + * item. + * + * @since 3.15.0 + */ + // Render a completion as obsolete, usually using a strike-out. + ComplDeprecated CompletionItemTag = 1 // line 13277 + // How a completion was triggered + /* + * Completion was triggered by typing an identifier (24x7 code + * complete), manual invocation (e.g Ctrl+Space) or via API. + */ + Invoked CompletionTriggerKind = 1 // line 13588 + /* + * Completion was triggered by a trigger character specified by + * the `triggerCharacters` properties of the `CompletionRegistrationOptions`. + */ + TriggerCharacter CompletionTriggerKind = 2 // line 13593 + // Completion was re-triggered as current completion list is incomplete + TriggerForIncompleteCompletions CompletionTriggerKind = 3 // line 13598 + // The diagnostic's severity. + // Reports an error. + SeverityError DiagnosticSeverity = 1 // line 13537 + // Reports a warning. + SeverityWarning DiagnosticSeverity = 2 // line 13542 + // Reports an information. + SeverityInformation DiagnosticSeverity = 3 // line 13547 + // Reports a hint. + SeverityHint DiagnosticSeverity = 4 // line 13552 + /* + * The diagnostic tags. + * + * @since 3.15.0 + */ + /* + * Unused or unnecessary code. + * + * Clients are allowed to render diagnostics with this tag faded out instead of having + * an error squiggle. + */ + Unnecessary DiagnosticTag = 1 // line 13567 + /* + * Deprecated or obsolete code. + * + * Clients are allowed to rendered diagnostics with this tag strike through. + */ + Deprecated DiagnosticTag = 2 // line 13572 + /* + * The document diagnostic report kinds. + * + * @since 3.17.0 + */ + /* + * A diagnostic report with a full + * set of problems. + */ + DiagnosticFull DocumentDiagnosticReportKind = "full" // line 12755 + /* + * A report indicating that the last + * returned report is still accurate. + */ + DiagnosticUnchanged DocumentDiagnosticReportKind = "unchanged" // line 12760 + // A document highlight kind. + // A textual occurrence. + Text DocumentHighlightKind = 1 // line 13334 + // Read-access of a symbol, like reading a variable. + Read DocumentHighlightKind = 2 // line 13339 + // Write-access of a symbol, like writing to a variable. + Write DocumentHighlightKind = 3 // line 13344 + // Predefined error codes. + ParseError ErrorCodes = -32700 // line 12776 + InvalidRequest ErrorCodes = -32600 // line 12780 + MethodNotFound ErrorCodes = -32601 // line 12784 + InvalidParams ErrorCodes = -32602 // line 12788 + InternalError ErrorCodes = -32603 // line 12792 + /* + * Error code indicating that a server received a notification or + * request before the server has received the `initialize` request. + */ + ServerNotInitialized ErrorCodes = -32002 // line 12796 + UnknownErrorCode ErrorCodes = -32001 // line 12801 + /* + * Applying the workspace change is simply aborted if one of the changes provided + * fails. All operations executed before the failing operation stay executed. + */ + Abort FailureHandlingKind = "abort" // line 13726 + /* + * All operations are executed transactional. That means they either all + * succeed or no changes at all are applied to the workspace. + */ + Transactional FailureHandlingKind = "transactional" // line 13731 + /* + * If the workspace edit contains only textual file changes they are executed transactional. + * If resource changes (create, rename or delete file) are part of the change the failure + * handling strategy is abort. + */ + TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" // line 13736 + /* + * The client tries to undo the operations already executed. But there is no + * guarantee that this is succeeding. + */ + Undo FailureHandlingKind = "undo" // line 13741 + // The file event type + // The file got created. + Created FileChangeType = 1 // line 13487 + // The file got changed. + Changed FileChangeType = 2 // line 13492 + // The file got deleted. + Deleted FileChangeType = 3 // line 13497 + /* + * A pattern kind describing if a glob pattern matches a file a folder or + * both. + * + * @since 3.16.0 + */ + // The pattern matches a file only. + FilePattern FileOperationPatternKind = "file" // line 13660 + // The pattern matches a folder only. + FolderPattern FileOperationPatternKind = "folder" // line 13665 + // A set of predefined range kinds. + // Folding range for a comment + Comment FoldingRangeKind = "comment" // line 12848 + // Folding range for an import or include + Imports FoldingRangeKind = "imports" // line 12853 + // Folding range for a region (e.g. `#region`) + Region FoldingRangeKind = "region" // line 12858 + /* + * Inlay hint kinds. + * + * @since 3.17.0 + */ + // An inlay hint that for a type annotation. + Type InlayHintKind = 1 // line 13066 + // An inlay hint that is for a parameter. + Parameter InlayHintKind = 2 // line 13071 + /* + * Defines whether the insert text in a completion item should be interpreted as + * plain text or a snippet. + */ + // The primary text to be inserted is treated as a plain string. + PlainTextTextFormat InsertTextFormat = 1 // line 13293 + /* + * The primary text to be inserted is treated as a snippet. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Placeholders with equal identifiers are linked, + * that is typing in one will update others too. + * + * See also: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#snippet_syntax + */ + SnippetTextFormat InsertTextFormat = 2 // line 13298 + /* + * How whitespace and indentation is handled during completion + * item insertion. + * + * @since 3.16.0 + */ + /* + * The insertion or replace strings is taken as it is. If the + * value is multi line the lines below the cursor will be + * inserted using the indentation defined in the string value. + * The client will not apply any kind of adjustments to the + * string. + */ + AsIs InsertTextMode = 1 // line 13313 + /* + * The editor adjusts leading whitespace of new lines so that + * they match the indentation up to the cursor of the line for + * which the item is accepted. + * + * Consider a line like this: <2tabs><3tabs>foo. Accepting a + * multi line completion item is indented using 2 tabs and all + * following lines inserted will be indented using 2 tabs as well. + */ + AdjustIndentation InsertTextMode = 2 // line 13318 + /* + * A request failed but it was syntactically correct, e.g the + * method name was known and the parameters were valid. The error + * message should contain human readable information about why + * the request failed. + * + * @since 3.17.0 + */ + RequestFailed LSPErrorCodes = -32803 // line 12816 + /* + * The server cancelled the request. This error code should + * only be used for requests that explicitly support being + * server cancellable. + * + * @since 3.17.0 + */ + ServerCancelled LSPErrorCodes = -32802 // line 12822 + /* + * The server detected that the content of a document got + * modified outside normal conditions. A server should + * NOT send this error code if it detects a content change + * in it unprocessed messages. The result even computed + * on an older state might still be useful for the client. + * + * If a client decides that a result is not of any use anymore + * the client should cancel the request. + */ + ContentModified LSPErrorCodes = -32801 // line 12828 + /* + * The client has canceled a request and a server as detected + * the cancel. + */ + RequestCancelled LSPErrorCodes = -32800 // line 12833 + /* + * Describes the content type that a client supports in various + * result literals like `Hover`, `ParameterInfo` or `CompletionItem`. + * + * Please note that `MarkupKinds` must not start with a `$`. This kinds + * are reserved for internal usage. + */ + // Plain text is supported as a content format + PlainText MarkupKind = "plaintext" // line 13440 + // Markdown is supported as a content format + Markdown MarkupKind = "markdown" // line 13445 + // The message type + // An error message. + Error MessageType = 1 // line 13087 + // A warning message. + Warning MessageType = 2 // line 13092 + // An information message. + Info MessageType = 3 // line 13097 + // A log message. + Log MessageType = 4 // line 13102 + /* + * The moniker kind. + * + * @since 3.16.0 + */ + // The moniker represent a symbol that is imported into a project + Import MonikerKind = "import" // line 13040 + // The moniker represents a symbol that is exported from a project + Export MonikerKind = "export" // line 13045 + /* + * The moniker represents a symbol that is local to a project (e.g. a local + * variable of a function, a class not visible outside the project, ...) + */ + Local MonikerKind = "local" // line 13050 + /* + * A notebook cell kind. + * + * @since 3.17.0 + */ + // A markup-cell is formatted source that is used for display. + Markup NotebookCellKind = 1 // line 13681 + // A code-cell is source code. + Code NotebookCellKind = 2 // line 13686 + /* + * A set of predefined position encoding kinds. + * + * @since 3.17.0 + */ + // Character offsets count UTF-8 code units. + UTF8 PositionEncodingKind = "utf-8" // line 13460 + /* + * Character offsets count UTF-16 code units. + * + * This is the default and must always be supported + * by servers + */ + UTF16 PositionEncodingKind = "utf-16" // line 13465 + /* + * Character offsets count UTF-32 code units. + * + * Implementation note: these are the same as Unicode code points, + * so this `PositionEncodingKind` may also be used for an + * encoding-agnostic representation of character offsets. + */ + UTF32 PositionEncodingKind = "utf-32" // line 13470 + // Supports creating new files and folders. + Create ResourceOperationKind = "create" // line 13702 + // Supports renaming existing files and folders. + Rename ResourceOperationKind = "rename" // line 13707 + // Supports deleting existing files and folders. + Delete ResourceOperationKind = "delete" // line 13712 + /* + * A set of predefined token modifiers. This set is not fixed + * an clients can specify additional token types via the + * corresponding client capabilities. + * + * @since 3.16.0 + */ + ModDeclaration SemanticTokenModifiers = "declaration" // line 12703 + ModDefinition SemanticTokenModifiers = "definition" // line 12707 + ModReadonly SemanticTokenModifiers = "readonly" // line 12711 + ModStatic SemanticTokenModifiers = "static" // line 12715 + ModDeprecated SemanticTokenModifiers = "deprecated" // line 12719 + ModAbstract SemanticTokenModifiers = "abstract" // line 12723 + ModAsync SemanticTokenModifiers = "async" // line 12727 + ModModification SemanticTokenModifiers = "modification" // line 12731 + ModDocumentation SemanticTokenModifiers = "documentation" // line 12735 + ModDefaultLibrary SemanticTokenModifiers = "defaultLibrary" // line 12739 + /* + * A set of predefined token types. This set is not fixed + * an clients can specify additional token types via the + * corresponding client capabilities. + * + * @since 3.16.0 + */ + NamespaceType SemanticTokenTypes = "namespace" // line 12596 + /* + * Represents a generic type. Acts as a fallback for types which can't be mapped to + * a specific type like class or enum. + */ + TypeType SemanticTokenTypes = "type" // line 12600 + ClassType SemanticTokenTypes = "class" // line 12605 + EnumType SemanticTokenTypes = "enum" // line 12609 + InterfaceType SemanticTokenTypes = "interface" // line 12613 + StructType SemanticTokenTypes = "struct" // line 12617 + TypeParameterType SemanticTokenTypes = "typeParameter" // line 12621 + ParameterType SemanticTokenTypes = "parameter" // line 12625 + VariableType SemanticTokenTypes = "variable" // line 12629 + PropertyType SemanticTokenTypes = "property" // line 12633 + EnumMemberType SemanticTokenTypes = "enumMember" // line 12637 + EventType SemanticTokenTypes = "event" // line 12641 + FunctionType SemanticTokenTypes = "function" // line 12645 + MethodType SemanticTokenTypes = "method" // line 12649 + MacroType SemanticTokenTypes = "macro" // line 12653 + KeywordType SemanticTokenTypes = "keyword" // line 12657 + ModifierType SemanticTokenTypes = "modifier" // line 12661 + CommentType SemanticTokenTypes = "comment" // line 12665 + StringType SemanticTokenTypes = "string" // line 12669 + NumberType SemanticTokenTypes = "number" // line 12673 + RegexpType SemanticTokenTypes = "regexp" // line 12677 + OperatorType SemanticTokenTypes = "operator" // line 12681 + // @since 3.17.0 + DecoratorType SemanticTokenTypes = "decorator" // line 12685 + /* + * How a signature help was triggered. + * + * @since 3.15.0 + */ + // Signature help was invoked manually by the user or by a command. + SigInvoked SignatureHelpTriggerKind = 1 // line 13613 + // Signature help was triggered by a trigger character. + SigTriggerCharacter SignatureHelpTriggerKind = 2 // line 13618 + // Signature help was triggered by the cursor moving or by the document content changing. + SigContentChange SignatureHelpTriggerKind = 3 // line 13623 + // A symbol kind. + File SymbolKind = 1 // line 12874 + Module SymbolKind = 2 // line 12878 + Namespace SymbolKind = 3 // line 12882 + Package SymbolKind = 4 // line 12886 + Class SymbolKind = 5 // line 12890 + Method SymbolKind = 6 // line 12894 + Property SymbolKind = 7 // line 12898 + Field SymbolKind = 8 // line 12902 + Constructor SymbolKind = 9 // line 12906 + Enum SymbolKind = 10 // line 12910 + Interface SymbolKind = 11 // line 12914 + Function SymbolKind = 12 // line 12918 + Variable SymbolKind = 13 // line 12922 + Constant SymbolKind = 14 // line 12926 + String SymbolKind = 15 // line 12930 + Number SymbolKind = 16 // line 12934 + Boolean SymbolKind = 17 // line 12938 + Array SymbolKind = 18 // line 12942 + Object SymbolKind = 19 // line 12946 + Key SymbolKind = 20 // line 12950 + Null SymbolKind = 21 // line 12954 + EnumMember SymbolKind = 22 // line 12958 + Struct SymbolKind = 23 // line 12962 + Event SymbolKind = 24 // line 12966 + Operator SymbolKind = 25 // line 12970 + TypeParameter SymbolKind = 26 // line 12974 + /* + * Symbol tags are extra annotations that tweak the rendering of a symbol. + * + * @since 3.16 + */ + // Render a symbol as obsolete, usually using a strike-out. + DeprecatedSymbol SymbolTag = 1 // line 12988 + // Represents reasons why a text document is saved. + /* + * Manually triggered, e.g. by the user pressing save, by starting debugging, + * or by an API call. + */ + Manual TextDocumentSaveReason = 1 // line 13142 + // Automatic after a delay. + AfterDelay TextDocumentSaveReason = 2 // line 13147 + // When the editor lost focus. + FocusOut TextDocumentSaveReason = 3 // line 13152 + /* + * Defines how the host (editor) should sync + * document changes to the language server. + */ + // Documents should not be synced at all. + None TextDocumentSyncKind = 0 // line 13117 + /* + * Documents are synced by always sending the full content + * of the document. + */ + Full TextDocumentSyncKind = 1 // line 13122 + /* + * Documents are synced by sending the full content on open. + * After that only incremental updates to the document are + * send. + */ + Incremental TextDocumentSyncKind = 2 // line 13127 + Relative TokenFormat = "relative" // line 13769 + // Turn tracing off. + Off TraceValues = "off" // line 13416 + // Trace messages only. + Messages TraceValues = "messages" // line 13421 + // Verbose message tracing. + Verbose TraceValues = "verbose" // line 13426 + /* + * Moniker uniqueness level to define scope of the moniker. + * + * @since 3.16.0 + */ + // The moniker is only unique inside a document + Document UniquenessLevel = "document" // line 13004 + // The moniker is unique inside a project for which a dump got created + Project UniquenessLevel = "project" // line 13009 + // The moniker is unique inside the group to which a project belongs + Group UniquenessLevel = "group" // line 13014 + // The moniker is unique inside the moniker scheme. + Scheme UniquenessLevel = "scheme" // line 13019 + // The moniker is globally unique + Global UniquenessLevel = "global" // line 13024 + // Interested in create events. + WatchCreate WatchKind = 1 // line 13512 + // Interested in change events + WatchChange WatchKind = 2 // line 13517 + // Interested in delete events + WatchDelete WatchKind = 4 // line 13522 +) diff --git a/gopls/golang_org_x_tools_gopls/lsp/protocol/tsserver.go b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsserver.go new file mode 100644 index 0000000..294c4fc --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/protocol/tsserver.go @@ -0,0 +1,1186 @@ +// Copyright 2019-2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protocol + +// Code generated from version 3.17.0 of protocol/metaModel.json. +// git hash 8de18faed635819dd2bc631d2c26ce4a18f7cf4a (as of Fri Sep 16 13:04:31 2022) +// Code generated; DO NOT EDIT. + +import ( + "context" + "encoding/json" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/jsonrpc2" +) + +type Server interface { + Progress(context.Context, *ProgressParams) error // $/progress + SetTrace(context.Context, *SetTraceParams) error // $/setTrace + IncomingCalls(context.Context, *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall, error) // callHierarchy/incomingCalls + OutgoingCalls(context.Context, *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall, error) // callHierarchy/outgoingCalls + ResolveCodeAction(context.Context, *CodeAction) (*CodeAction, error) // codeAction/resolve + ResolveCodeLens(context.Context, *CodeLens) (*CodeLens, error) // codeLens/resolve + ResolveCompletionItem(context.Context, *CompletionItem) (*CompletionItem, error) // completionItem/resolve + ResolveDocumentLink(context.Context, *DocumentLink) (*DocumentLink, error) // documentLink/resolve + Exit(context.Context) error // exit + Initialize(context.Context, *ParamInitialize) (*InitializeResult, error) // initialize + Initialized(context.Context, *InitializedParams) error // initialized + Resolve(context.Context, *InlayHint) (*InlayHint, error) // inlayHint/resolve + DidChangeNotebookDocument(context.Context, *DidChangeNotebookDocumentParams) error // notebookDocument/didChange + DidCloseNotebookDocument(context.Context, *DidCloseNotebookDocumentParams) error // notebookDocument/didClose + DidOpenNotebookDocument(context.Context, *DidOpenNotebookDocumentParams) error // notebookDocument/didOpen + DidSaveNotebookDocument(context.Context, *DidSaveNotebookDocumentParams) error // notebookDocument/didSave + Shutdown(context.Context) error // shutdown + CodeAction(context.Context, *CodeActionParams) ([]CodeAction, error) // textDocument/codeAction + CodeLens(context.Context, *CodeLensParams) ([]CodeLens, error) // textDocument/codeLens + ColorPresentation(context.Context, *ColorPresentationParams) ([]ColorPresentation, error) // textDocument/colorPresentation + Completion(context.Context, *CompletionParams) (*CompletionList, error) // textDocument/completion + Declaration(context.Context, *DeclarationParams) (*Or_textDocument_declaration, error) // textDocument/declaration + Definition(context.Context, *DefinitionParams) ([]Location, error) // textDocument/definition + Diagnostic(context.Context, *string) (*string, error) // textDocument/diagnostic + DidChange(context.Context, *DidChangeTextDocumentParams) error // textDocument/didChange + DidClose(context.Context, *DidCloseTextDocumentParams) error // textDocument/didClose + DidOpen(context.Context, *DidOpenTextDocumentParams) error // textDocument/didOpen + DidSave(context.Context, *DidSaveTextDocumentParams) error // textDocument/didSave + DocumentColor(context.Context, *DocumentColorParams) ([]ColorInformation, error) // textDocument/documentColor + DocumentHighlight(context.Context, *DocumentHighlightParams) ([]DocumentHighlight, error) // textDocument/documentHighlight + DocumentLink(context.Context, *DocumentLinkParams) ([]DocumentLink, error) // textDocument/documentLink + DocumentSymbol(context.Context, *DocumentSymbolParams) ([]interface{}, error) // textDocument/documentSymbol + FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange, error) // textDocument/foldingRange + Formatting(context.Context, *DocumentFormattingParams) ([]TextEdit, error) // textDocument/formatting + Hover(context.Context, *HoverParams) (*Hover, error) // textDocument/hover + Implementation(context.Context, *ImplementationParams) ([]Location, error) // textDocument/implementation + InlayHint(context.Context, *InlayHintParams) ([]InlayHint, error) // textDocument/inlayHint + InlineValue(context.Context, *InlineValueParams) ([]InlineValue, error) // textDocument/inlineValue + LinkedEditingRange(context.Context, *LinkedEditingRangeParams) (*LinkedEditingRanges, error) // textDocument/linkedEditingRange + Moniker(context.Context, *MonikerParams) ([]Moniker, error) // textDocument/moniker + OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit, error) // textDocument/onTypeFormatting + PrepareCallHierarchy(context.Context, *CallHierarchyPrepareParams) ([]CallHierarchyItem, error) // textDocument/prepareCallHierarchy + PrepareRename(context.Context, *PrepareRenameParams) (*PrepareRename2Gn, error) // textDocument/prepareRename + PrepareTypeHierarchy(context.Context, *TypeHierarchyPrepareParams) ([]TypeHierarchyItem, error) // textDocument/prepareTypeHierarchy + RangeFormatting(context.Context, *DocumentRangeFormattingParams) ([]TextEdit, error) // textDocument/rangeFormatting + References(context.Context, *ReferenceParams) ([]Location, error) // textDocument/references + Rename(context.Context, *RenameParams) (*WorkspaceEdit, error) // textDocument/rename + SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange, error) // textDocument/selectionRange + SemanticTokensFull(context.Context, *SemanticTokensParams) (*SemanticTokens, error) // textDocument/semanticTokens/full + SemanticTokensFullDelta(context.Context, *SemanticTokensDeltaParams) (interface{}, error) // textDocument/semanticTokens/full/delta + SemanticTokensRange(context.Context, *SemanticTokensRangeParams) (*SemanticTokens, error) // textDocument/semanticTokens/range + SignatureHelp(context.Context, *SignatureHelpParams) (*SignatureHelp, error) // textDocument/signatureHelp + TypeDefinition(context.Context, *TypeDefinitionParams) ([]Location, error) // textDocument/typeDefinition + WillSave(context.Context, *WillSaveTextDocumentParams) error // textDocument/willSave + WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit, error) // textDocument/willSaveWaitUntil + Subtypes(context.Context, *TypeHierarchySubtypesParams) ([]TypeHierarchyItem, error) // typeHierarchy/subtypes + Supertypes(context.Context, *TypeHierarchySupertypesParams) ([]TypeHierarchyItem, error) // typeHierarchy/supertypes + WorkDoneProgressCancel(context.Context, *WorkDoneProgressCancelParams) error // window/workDoneProgress/cancel + DiagnosticWorkspace(context.Context, *WorkspaceDiagnosticParams) (*WorkspaceDiagnosticReport, error) // workspace/diagnostic + DiagnosticRefresh(context.Context) error // workspace/diagnostic/refresh + DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error // workspace/didChangeConfiguration + DidChangeWatchedFiles(context.Context, *DidChangeWatchedFilesParams) error // workspace/didChangeWatchedFiles + DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error // workspace/didChangeWorkspaceFolders + DidCreateFiles(context.Context, *CreateFilesParams) error // workspace/didCreateFiles + DidDeleteFiles(context.Context, *DeleteFilesParams) error // workspace/didDeleteFiles + DidRenameFiles(context.Context, *RenameFilesParams) error // workspace/didRenameFiles + ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error) // workspace/executeCommand + InlayHintRefresh(context.Context) error // workspace/inlayHint/refresh + InlineValueRefresh(context.Context) error // workspace/inlineValue/refresh + SemanticTokensRefresh(context.Context) error // workspace/semanticTokens/refresh + Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation, error) // workspace/symbol + WillCreateFiles(context.Context, *CreateFilesParams) (*WorkspaceEdit, error) // workspace/willCreateFiles + WillDeleteFiles(context.Context, *DeleteFilesParams) (*WorkspaceEdit, error) // workspace/willDeleteFiles + WillRenameFiles(context.Context, *RenameFilesParams) (*WorkspaceEdit, error) // workspace/willRenameFiles + ResolveWorkspaceSymbol(context.Context, *WorkspaceSymbol) (*WorkspaceSymbol, error) // workspaceSymbol/resolve + NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) +} + +func serverDispatch(ctx context.Context, server Server, reply jsonrpc2.Replier, r jsonrpc2.Request) (bool, error) { + switch r.Method() { + case "$/progress": + var params ProgressParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.Progress(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "$/setTrace": + var params SetTraceParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.SetTrace(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "callHierarchy/incomingCalls": + var params CallHierarchyIncomingCallsParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.IncomingCalls(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "callHierarchy/outgoingCalls": + var params CallHierarchyOutgoingCallsParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.OutgoingCalls(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "codeAction/resolve": + var params CodeAction + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.ResolveCodeAction(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "codeLens/resolve": + var params CodeLens + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.ResolveCodeLens(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "completionItem/resolve": + var params CompletionItem + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.ResolveCompletionItem(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "documentLink/resolve": + var params DocumentLink + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.ResolveDocumentLink(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "exit": + err := server.Exit(ctx) + return true, reply(ctx, nil, err) // 236 + case "initialize": + var params ParamInitialize + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Initialize(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "initialized": + var params InitializedParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.Initialized(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "inlayHint/resolve": + var params InlayHint + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Resolve(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "notebookDocument/didChange": + var params DidChangeNotebookDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidChangeNotebookDocument(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "notebookDocument/didClose": + var params DidCloseNotebookDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidCloseNotebookDocument(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "notebookDocument/didOpen": + var params DidOpenNotebookDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidOpenNotebookDocument(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "notebookDocument/didSave": + var params DidSaveNotebookDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidSaveNotebookDocument(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "shutdown": + err := server.Shutdown(ctx) + return true, reply(ctx, nil, err) // 176 + case "textDocument/codeAction": + var params CodeActionParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.CodeAction(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/codeLens": + var params CodeLensParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.CodeLens(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/colorPresentation": + var params ColorPresentationParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.ColorPresentation(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/completion": + var params CompletionParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Completion(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/declaration": + var params DeclarationParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Declaration(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/definition": + var params DefinitionParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Definition(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/diagnostic": + var params string + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Diagnostic(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/didChange": + var params DidChangeTextDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidChange(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "textDocument/didClose": + var params DidCloseTextDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidClose(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "textDocument/didOpen": + var params DidOpenTextDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidOpen(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "textDocument/didSave": + var params DidSaveTextDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidSave(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "textDocument/documentColor": + var params DocumentColorParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.DocumentColor(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/documentHighlight": + var params DocumentHighlightParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.DocumentHighlight(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/documentLink": + var params DocumentLinkParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.DocumentLink(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/documentSymbol": + var params DocumentSymbolParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.DocumentSymbol(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/foldingRange": + var params FoldingRangeParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.FoldingRange(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/formatting": + var params DocumentFormattingParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Formatting(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/hover": + var params HoverParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Hover(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/implementation": + var params ImplementationParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Implementation(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/inlayHint": + var params InlayHintParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.InlayHint(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/inlineValue": + var params InlineValueParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.InlineValue(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/linkedEditingRange": + var params LinkedEditingRangeParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.LinkedEditingRange(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/moniker": + var params MonikerParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Moniker(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/onTypeFormatting": + var params DocumentOnTypeFormattingParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.OnTypeFormatting(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/prepareCallHierarchy": + var params CallHierarchyPrepareParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.PrepareCallHierarchy(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/prepareRename": + var params PrepareRenameParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.PrepareRename(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/prepareTypeHierarchy": + var params TypeHierarchyPrepareParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.PrepareTypeHierarchy(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/rangeFormatting": + var params DocumentRangeFormattingParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.RangeFormatting(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/references": + var params ReferenceParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.References(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/rename": + var params RenameParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Rename(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/selectionRange": + var params SelectionRangeParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SelectionRange(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/semanticTokens/full": + var params SemanticTokensParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SemanticTokensFull(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/semanticTokens/full/delta": + var params SemanticTokensDeltaParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SemanticTokensFullDelta(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/semanticTokens/range": + var params SemanticTokensRangeParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SemanticTokensRange(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/signatureHelp": + var params SignatureHelpParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.SignatureHelp(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/typeDefinition": + var params TypeDefinitionParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.TypeDefinition(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "textDocument/willSave": + var params WillSaveTextDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.WillSave(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "textDocument/willSaveWaitUntil": + var params WillSaveTextDocumentParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.WillSaveWaitUntil(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "typeHierarchy/subtypes": + var params TypeHierarchySubtypesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Subtypes(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "typeHierarchy/supertypes": + var params TypeHierarchySupertypesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Supertypes(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "window/workDoneProgress/cancel": + var params WorkDoneProgressCancelParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.WorkDoneProgressCancel(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "workspace/diagnostic": + var params WorkspaceDiagnosticParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.DiagnosticWorkspace(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspace/diagnostic/refresh": + err := server.DiagnosticRefresh(ctx) + return true, reply(ctx, nil, err) // 170 + case "workspace/didChangeConfiguration": + var params DidChangeConfigurationParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidChangeConfiguration(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "workspace/didChangeWatchedFiles": + var params DidChangeWatchedFilesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidChangeWatchedFiles(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "workspace/didChangeWorkspaceFolders": + var params DidChangeWorkspaceFoldersParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidChangeWorkspaceFolders(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "workspace/didCreateFiles": + var params CreateFilesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidCreateFiles(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "workspace/didDeleteFiles": + var params DeleteFilesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidDeleteFiles(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "workspace/didRenameFiles": + var params RenameFilesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + err := server.DidRenameFiles(ctx, ¶ms) + return true, reply(ctx, nil, err) // 231 + case "workspace/executeCommand": + var params ExecuteCommandParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.ExecuteCommand(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspace/inlayHint/refresh": + err := server.InlayHintRefresh(ctx) + return true, reply(ctx, nil, err) // 170 + case "workspace/inlineValue/refresh": + err := server.InlineValueRefresh(ctx) + return true, reply(ctx, nil, err) // 170 + case "workspace/semanticTokens/refresh": + err := server.SemanticTokensRefresh(ctx) + return true, reply(ctx, nil, err) // 170 + case "workspace/symbol": + var params WorkspaceSymbolParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.Symbol(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspace/willCreateFiles": + var params CreateFilesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.WillCreateFiles(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspace/willDeleteFiles": + var params DeleteFilesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.WillDeleteFiles(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspace/willRenameFiles": + var params RenameFilesParams + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.WillRenameFiles(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + case "workspaceSymbol/resolve": + var params WorkspaceSymbol + if err := json.Unmarshal(r.Params(), ¶ms); err != nil { + return true, sendParseError(ctx, reply, err) + } + resp, err := server.ResolveWorkspaceSymbol(ctx, ¶ms) + if err != nil { + return true, reply(ctx, nil, err) + } + return true, reply(ctx, resp, nil) // 146 + default: + return false, nil + } +} + +func (s *serverDispatcher) Progress(ctx context.Context, params *ProgressParams) error { + return s.sender.Notify(ctx, "$/progress", params) +} // 244 +func (s *serverDispatcher) SetTrace(ctx context.Context, params *SetTraceParams) error { + return s.sender.Notify(ctx, "$/setTrace", params) +} // 244 +func (s *serverDispatcher) IncomingCalls(ctx context.Context, params *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall, error) { + var result []CallHierarchyIncomingCall + if err := s.sender.Call(ctx, "callHierarchy/incomingCalls", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) OutgoingCalls(ctx context.Context, params *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall, error) { + var result []CallHierarchyOutgoingCall + if err := s.sender.Call(ctx, "callHierarchy/outgoingCalls", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) ResolveCodeAction(ctx context.Context, params *CodeAction) (*CodeAction, error) { + var result *CodeAction + if err := s.sender.Call(ctx, "codeAction/resolve", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) ResolveCodeLens(ctx context.Context, params *CodeLens) (*CodeLens, error) { + var result *CodeLens + if err := s.sender.Call(ctx, "codeLens/resolve", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) ResolveCompletionItem(ctx context.Context, params *CompletionItem) (*CompletionItem, error) { + var result *CompletionItem + if err := s.sender.Call(ctx, "completionItem/resolve", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) ResolveDocumentLink(ctx context.Context, params *DocumentLink) (*DocumentLink, error) { + var result *DocumentLink + if err := s.sender.Call(ctx, "documentLink/resolve", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Exit(ctx context.Context) error { + return s.sender.Notify(ctx, "exit", nil) +} // 249 +func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitialize) (*InitializeResult, error) { + var result *InitializeResult + if err := s.sender.Call(ctx, "initialize", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error { + return s.sender.Notify(ctx, "initialized", params) +} // 244 +func (s *serverDispatcher) Resolve(ctx context.Context, params *InlayHint) (*InlayHint, error) { + var result *InlayHint + if err := s.sender.Call(ctx, "inlayHint/resolve", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) DidChangeNotebookDocument(ctx context.Context, params *DidChangeNotebookDocumentParams) error { + return s.sender.Notify(ctx, "notebookDocument/didChange", params) +} // 244 +func (s *serverDispatcher) DidCloseNotebookDocument(ctx context.Context, params *DidCloseNotebookDocumentParams) error { + return s.sender.Notify(ctx, "notebookDocument/didClose", params) +} // 244 +func (s *serverDispatcher) DidOpenNotebookDocument(ctx context.Context, params *DidOpenNotebookDocumentParams) error { + return s.sender.Notify(ctx, "notebookDocument/didOpen", params) +} // 244 +func (s *serverDispatcher) DidSaveNotebookDocument(ctx context.Context, params *DidSaveNotebookDocumentParams) error { + return s.sender.Notify(ctx, "notebookDocument/didSave", params) +} // 244 +func (s *serverDispatcher) Shutdown(ctx context.Context) error { + return s.sender.Call(ctx, "shutdown", nil, nil) +} // 209 +func (s *serverDispatcher) CodeAction(ctx context.Context, params *CodeActionParams) ([]CodeAction, error) { + var result []CodeAction + if err := s.sender.Call(ctx, "textDocument/codeAction", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) CodeLens(ctx context.Context, params *CodeLensParams) ([]CodeLens, error) { + var result []CodeLens + if err := s.sender.Call(ctx, "textDocument/codeLens", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) ColorPresentation(ctx context.Context, params *ColorPresentationParams) ([]ColorPresentation, error) { + var result []ColorPresentation + if err := s.sender.Call(ctx, "textDocument/colorPresentation", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Completion(ctx context.Context, params *CompletionParams) (*CompletionList, error) { + var result *CompletionList + if err := s.sender.Call(ctx, "textDocument/completion", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Declaration(ctx context.Context, params *DeclarationParams) (*Or_textDocument_declaration, error) { + var result *Or_textDocument_declaration + if err := s.sender.Call(ctx, "textDocument/declaration", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Definition(ctx context.Context, params *DefinitionParams) ([]Location, error) { + var result []Location + if err := s.sender.Call(ctx, "textDocument/definition", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Diagnostic(ctx context.Context, params *string) (*string, error) { + var result *string + if err := s.sender.Call(ctx, "textDocument/diagnostic", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) DidChange(ctx context.Context, params *DidChangeTextDocumentParams) error { + return s.sender.Notify(ctx, "textDocument/didChange", params) +} // 244 +func (s *serverDispatcher) DidClose(ctx context.Context, params *DidCloseTextDocumentParams) error { + return s.sender.Notify(ctx, "textDocument/didClose", params) +} // 244 +func (s *serverDispatcher) DidOpen(ctx context.Context, params *DidOpenTextDocumentParams) error { + return s.sender.Notify(ctx, "textDocument/didOpen", params) +} // 244 +func (s *serverDispatcher) DidSave(ctx context.Context, params *DidSaveTextDocumentParams) error { + return s.sender.Notify(ctx, "textDocument/didSave", params) +} // 244 +func (s *serverDispatcher) DocumentColor(ctx context.Context, params *DocumentColorParams) ([]ColorInformation, error) { + var result []ColorInformation + if err := s.sender.Call(ctx, "textDocument/documentColor", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) DocumentHighlight(ctx context.Context, params *DocumentHighlightParams) ([]DocumentHighlight, error) { + var result []DocumentHighlight + if err := s.sender.Call(ctx, "textDocument/documentHighlight", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) DocumentLink(ctx context.Context, params *DocumentLinkParams) ([]DocumentLink, error) { + var result []DocumentLink + if err := s.sender.Call(ctx, "textDocument/documentLink", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) DocumentSymbol(ctx context.Context, params *DocumentSymbolParams) ([]interface{}, error) { + var result []interface{} + if err := s.sender.Call(ctx, "textDocument/documentSymbol", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) FoldingRange(ctx context.Context, params *FoldingRangeParams) ([]FoldingRange, error) { + var result []FoldingRange + if err := s.sender.Call(ctx, "textDocument/foldingRange", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Formatting(ctx context.Context, params *DocumentFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.sender.Call(ctx, "textDocument/formatting", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Hover(ctx context.Context, params *HoverParams) (*Hover, error) { + var result *Hover + if err := s.sender.Call(ctx, "textDocument/hover", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Implementation(ctx context.Context, params *ImplementationParams) ([]Location, error) { + var result []Location + if err := s.sender.Call(ctx, "textDocument/implementation", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) InlayHint(ctx context.Context, params *InlayHintParams) ([]InlayHint, error) { + var result []InlayHint + if err := s.sender.Call(ctx, "textDocument/inlayHint", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) InlineValue(ctx context.Context, params *InlineValueParams) ([]InlineValue, error) { + var result []InlineValue + if err := s.sender.Call(ctx, "textDocument/inlineValue", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) LinkedEditingRange(ctx context.Context, params *LinkedEditingRangeParams) (*LinkedEditingRanges, error) { + var result *LinkedEditingRanges + if err := s.sender.Call(ctx, "textDocument/linkedEditingRange", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Moniker(ctx context.Context, params *MonikerParams) ([]Moniker, error) { + var result []Moniker + if err := s.sender.Call(ctx, "textDocument/moniker", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) OnTypeFormatting(ctx context.Context, params *DocumentOnTypeFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.sender.Call(ctx, "textDocument/onTypeFormatting", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) PrepareCallHierarchy(ctx context.Context, params *CallHierarchyPrepareParams) ([]CallHierarchyItem, error) { + var result []CallHierarchyItem + if err := s.sender.Call(ctx, "textDocument/prepareCallHierarchy", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (*PrepareRename2Gn, error) { + var result *PrepareRename2Gn + if err := s.sender.Call(ctx, "textDocument/prepareRename", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) PrepareTypeHierarchy(ctx context.Context, params *TypeHierarchyPrepareParams) ([]TypeHierarchyItem, error) { + var result []TypeHierarchyItem + if err := s.sender.Call(ctx, "textDocument/prepareTypeHierarchy", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) RangeFormatting(ctx context.Context, params *DocumentRangeFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.sender.Call(ctx, "textDocument/rangeFormatting", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) References(ctx context.Context, params *ReferenceParams) ([]Location, error) { + var result []Location + if err := s.sender.Call(ctx, "textDocument/references", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Rename(ctx context.Context, params *RenameParams) (*WorkspaceEdit, error) { + var result *WorkspaceEdit + if err := s.sender.Call(ctx, "textDocument/rename", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) SelectionRange(ctx context.Context, params *SelectionRangeParams) ([]SelectionRange, error) { + var result []SelectionRange + if err := s.sender.Call(ctx, "textDocument/selectionRange", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) SemanticTokensFull(ctx context.Context, params *SemanticTokensParams) (*SemanticTokens, error) { + var result *SemanticTokens + if err := s.sender.Call(ctx, "textDocument/semanticTokens/full", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) SemanticTokensFullDelta(ctx context.Context, params *SemanticTokensDeltaParams) (interface{}, error) { + var result interface{} + if err := s.sender.Call(ctx, "textDocument/semanticTokens/full/delta", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) SemanticTokensRange(ctx context.Context, params *SemanticTokensRangeParams) (*SemanticTokens, error) { + var result *SemanticTokens + if err := s.sender.Call(ctx, "textDocument/semanticTokens/range", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) SignatureHelp(ctx context.Context, params *SignatureHelpParams) (*SignatureHelp, error) { + var result *SignatureHelp + if err := s.sender.Call(ctx, "textDocument/signatureHelp", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) TypeDefinition(ctx context.Context, params *TypeDefinitionParams) ([]Location, error) { + var result []Location + if err := s.sender.Call(ctx, "textDocument/typeDefinition", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) WillSave(ctx context.Context, params *WillSaveTextDocumentParams) error { + return s.sender.Notify(ctx, "textDocument/willSave", params) +} // 244 +func (s *serverDispatcher) WillSaveWaitUntil(ctx context.Context, params *WillSaveTextDocumentParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.sender.Call(ctx, "textDocument/willSaveWaitUntil", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Subtypes(ctx context.Context, params *TypeHierarchySubtypesParams) ([]TypeHierarchyItem, error) { + var result []TypeHierarchyItem + if err := s.sender.Call(ctx, "typeHierarchy/subtypes", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) Supertypes(ctx context.Context, params *TypeHierarchySupertypesParams) ([]TypeHierarchyItem, error) { + var result []TypeHierarchyItem + if err := s.sender.Call(ctx, "typeHierarchy/supertypes", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) WorkDoneProgressCancel(ctx context.Context, params *WorkDoneProgressCancelParams) error { + return s.sender.Notify(ctx, "window/workDoneProgress/cancel", params) +} // 244 +func (s *serverDispatcher) DiagnosticWorkspace(ctx context.Context, params *WorkspaceDiagnosticParams) (*WorkspaceDiagnosticReport, error) { + var result *WorkspaceDiagnosticReport + if err := s.sender.Call(ctx, "workspace/diagnostic", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) DiagnosticRefresh(ctx context.Context) error { + return s.sender.Call(ctx, "workspace/diagnostic/refresh", nil, nil) +} // 209 +func (s *serverDispatcher) DidChangeConfiguration(ctx context.Context, params *DidChangeConfigurationParams) error { + return s.sender.Notify(ctx, "workspace/didChangeConfiguration", params) +} // 244 +func (s *serverDispatcher) DidChangeWatchedFiles(ctx context.Context, params *DidChangeWatchedFilesParams) error { + return s.sender.Notify(ctx, "workspace/didChangeWatchedFiles", params) +} // 244 +func (s *serverDispatcher) DidChangeWorkspaceFolders(ctx context.Context, params *DidChangeWorkspaceFoldersParams) error { + return s.sender.Notify(ctx, "workspace/didChangeWorkspaceFolders", params) +} // 244 +func (s *serverDispatcher) DidCreateFiles(ctx context.Context, params *CreateFilesParams) error { + return s.sender.Notify(ctx, "workspace/didCreateFiles", params) +} // 244 +func (s *serverDispatcher) DidDeleteFiles(ctx context.Context, params *DeleteFilesParams) error { + return s.sender.Notify(ctx, "workspace/didDeleteFiles", params) +} // 244 +func (s *serverDispatcher) DidRenameFiles(ctx context.Context, params *RenameFilesParams) error { + return s.sender.Notify(ctx, "workspace/didRenameFiles", params) +} // 244 +func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCommandParams) (interface{}, error) { + var result interface{} + if err := s.sender.Call(ctx, "workspace/executeCommand", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) InlayHintRefresh(ctx context.Context) error { + return s.sender.Call(ctx, "workspace/inlayHint/refresh", nil, nil) +} // 209 +func (s *serverDispatcher) InlineValueRefresh(ctx context.Context) error { + return s.sender.Call(ctx, "workspace/inlineValue/refresh", nil, nil) +} // 209 +func (s *serverDispatcher) SemanticTokensRefresh(ctx context.Context) error { + return s.sender.Call(ctx, "workspace/semanticTokens/refresh", nil, nil) +} // 209 +func (s *serverDispatcher) Symbol(ctx context.Context, params *WorkspaceSymbolParams) ([]SymbolInformation, error) { + var result []SymbolInformation + if err := s.sender.Call(ctx, "workspace/symbol", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) WillCreateFiles(ctx context.Context, params *CreateFilesParams) (*WorkspaceEdit, error) { + var result *WorkspaceEdit + if err := s.sender.Call(ctx, "workspace/willCreateFiles", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) WillDeleteFiles(ctx context.Context, params *DeleteFilesParams) (*WorkspaceEdit, error) { + var result *WorkspaceEdit + if err := s.sender.Call(ctx, "workspace/willDeleteFiles", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) WillRenameFiles(ctx context.Context, params *RenameFilesParams) (*WorkspaceEdit, error) { + var result *WorkspaceEdit + if err := s.sender.Call(ctx, "workspace/willRenameFiles", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) ResolveWorkspaceSymbol(ctx context.Context, params *WorkspaceSymbol) (*WorkspaceSymbol, error) { + var result *WorkspaceSymbol + if err := s.sender.Call(ctx, "workspaceSymbol/resolve", params, &result); err != nil { + return nil, err + } + return result, nil +} // 169 +func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) { + var result interface{} + if err := s.sender.Call(ctx, method, params, &result); err != nil { + return nil, err + } + return result, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/safetoken/safetoken.go b/gopls/golang_org_x_tools_gopls/lsp/safetoken/safetoken.go new file mode 100644 index 0000000..6898df0 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/safetoken/safetoken.go @@ -0,0 +1,36 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package safetoken provides wrappers around methods in go/token, that return +// errors rather than panicking. +package safetoken + +import ( + "fmt" + "go/token" +) + +// Offset returns tok.Offset(pos), but first checks that the pos is in range +// for the given file. +func Offset(tf *token.File, pos token.Pos) (int, error) { + if !InRange(tf, pos) { + return -1, fmt.Errorf("pos %v is not in range for file [%v:%v)", pos, tf.Base(), tf.Base()+tf.Size()) + } + return tf.Offset(pos), nil +} + +// Pos returns tok.Pos(offset), but first checks that the offset is valid for +// the given file. +func Pos(tf *token.File, offset int) (token.Pos, error) { + if offset < 0 || offset > tf.Size() { + return token.NoPos, fmt.Errorf("offset %v is not in range for file of size %v", offset, tf.Size()) + } + return tf.Pos(offset), nil +} + +// InRange reports whether the given position is in the given token.File. +func InRange(tf *token.File, pos token.Pos) bool { + size := tf.Pos(tf.Size()) + return int(pos) >= tf.Base() && pos <= size +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/add_import.go b/gopls/golang_org_x_tools_gopls/lsp/source/add_import.go new file mode 100644 index 0000000..db81aca --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/add_import.go @@ -0,0 +1,26 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/imports" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +// AddImport adds a single import statement to the given file +func AddImport(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, importPath string) ([]protocol.TextEdit, error) { + _, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return nil, err + } + return ComputeOneImportFixEdits(snapshot, pgf, &imports.ImportFix{ + StmtInfo: imports.ImportInfo{ + ImportPath: importPath, + }, + FixType: imports.AddImport, + }) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/api_json.go b/gopls/golang_org_x_tools_gopls/lsp/source/api_json.go new file mode 100644 index 0000000..762054d --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/api_json.go @@ -0,0 +1,1114 @@ +// Code generated by "golang.org/x/tools/gopls/doc/generate"; DO NOT EDIT. + +package source + +var GeneratedAPIJSON = &APIJSON{ + Options: map[string][]*OptionJSON{ + "User": { + { + Name: "buildFlags", + Type: "[]string", + Doc: "buildFlags is the set of flags passed on to the build system when invoked.\nIt is applied to queries like `go list`, which is used when discovering files.\nThe most common use is to set `-tags`.\n", + Default: "[]", + Hierarchy: "build", + }, + { + Name: "env", + Type: "map[string]string", + Doc: "env adds environment variables to external commands run by `gopls`, most notably `go list`.\n", + Default: "{}", + Hierarchy: "build", + }, + { + Name: "directoryFilters", + Type: "[]string", + Doc: "directoryFilters can be used to exclude unwanted directories from the\nworkspace. By default, all directories are included. Filters are an\noperator, `+` to include and `-` to exclude, followed by a path prefix\nrelative to the workspace folder. They are evaluated in order, and\nthe last filter that applies to a path controls whether it is included.\nThe path prefix can be empty, so an initial `-` excludes everything.\n\nDirectoryFilters also supports the `**` operator to match 0 or more directories.\n\nExamples:\n\nExclude node_modules at current depth: `-node_modules`\n\nExclude node_modules at any depth: `-**/node_modules`\n\nInclude only project_a: `-` (exclude everything), `+project_a`\n\nInclude only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules`\n", + Default: "[\"-**/node_modules\"]", + Hierarchy: "build", + }, + { + Name: "templateExtensions", + Type: "[]string", + Doc: "templateExtensions gives the extensions of file names that are treateed\nas template files. (The extension\nis the part of the file name after the final dot.)\n", + Default: "[]", + Hierarchy: "build", + }, + { + Name: "memoryMode", + Type: "enum", + Doc: "memoryMode controls the tradeoff `gopls` makes between memory usage and\ncorrectness.\n\nValues other than `Normal` are untested and may break in surprising ways.\n", + EnumValues: []EnumValue{ + { + Value: "\"DegradeClosed\"", + Doc: "`\"DegradeClosed\"`: In DegradeClosed mode, `gopls` will collect less information about\npackages without open files. As a result, features like Find\nReferences and Rename will miss results in such packages.\n", + }, + {Value: "\"Normal\""}, + }, + Default: "\"Normal\"", + Status: "experimental", + Hierarchy: "build", + }, + { + Name: "expandWorkspaceToModule", + Type: "bool", + Doc: "expandWorkspaceToModule instructs `gopls` to adjust the scope of the\nworkspace to find the best available module root. `gopls` first looks for\na go.mod file in any parent directory of the workspace folder, expanding\nthe scope to that directory if it exists. If no viable parent directory is\nfound, gopls will check if there is exactly one child directory containing\na go.mod file, narrowing the scope to that directory if it exists.\n", + Default: "true", + Status: "experimental", + Hierarchy: "build", + }, + { + Name: "experimentalWorkspaceModule", + Type: "bool", + Doc: "experimentalWorkspaceModule opts a user into the experimental support\nfor multi-module workspaces.\n\nDeprecated: this feature is deprecated and will be removed in a future\nversion of gopls (https://go.dev/issue/55331).\n", + Default: "false", + Status: "experimental", + Hierarchy: "build", + }, + { + Name: "experimentalPackageCacheKey", + Type: "bool", + Doc: "experimentalPackageCacheKey controls whether to use a coarser cache key\nfor package type information to increase cache hits. This setting removes\nthe user's environment, build flags, and working directory from the cache\nkey, which should be a safe change as all relevant inputs into the type\nchecking pass are already hashed into the key. This is temporarily guarded\nby an experiment because caching behavior is subtle and difficult to\ncomprehensively test.\n", + Default: "true", + Status: "experimental", + Hierarchy: "build", + }, + { + Name: "allowModfileModifications", + Type: "bool", + Doc: "allowModfileModifications disables -mod=readonly, allowing imports from\nout-of-scope modules. This option will eventually be removed.\n", + Default: "false", + Status: "experimental", + Hierarchy: "build", + }, + { + Name: "allowImplicitNetworkAccess", + Type: "bool", + Doc: "allowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module\ndownloads rather than requiring user action. This option will eventually\nbe removed.\n", + Default: "false", + Status: "experimental", + Hierarchy: "build", + }, + { + Name: "experimentalUseInvalidMetadata", + Type: "bool", + Doc: "experimentalUseInvalidMetadata enables gopls to fall back on outdated\npackage metadata to provide editor features if the go command fails to\nload packages for some reason (like an invalid go.mod file).\n\nDeprecated: this setting is deprecated and will be removed in a future\nversion of gopls (https://go.dev/issue/55333).\n", + Default: "false", + Status: "experimental", + Hierarchy: "build", + }, + { + Name: "standaloneTags", + Type: "[]string", + Doc: "standaloneTags specifies a set of build constraints that identify\nindividual Go source files that make up the entire main package of an\nexecutable.\n\nA common example of standalone main files is the convention of using the\ndirective `//go:build ignore` to denote files that are not intended to be\nincluded in any package, for example because they are invoked directly by\nthe developer using `go run`.\n\nGopls considers a file to be a standalone main file if and only if it has\npackage name \"main\" and has a build directive of the exact form\n\"//go:build tag\" or \"// +build tag\", where tag is among the list of tags\nconfigured by this setting. Notably, if the build constraint is more\ncomplicated than a simple tag (such as the composite constraint\n`//go:build tag && go1.18`), the file is not considered to be a standalone\nmain file.\n\nThis setting is only supported when gopls is built with Go 1.16 or later.\n", + Default: "[\"ignore\"]", + Hierarchy: "build", + }, + { + Name: "hoverKind", + Type: "enum", + Doc: "hoverKind controls the information that appears in the hover text.\nSingleLine and Structured are intended for use only by authors of editor plugins.\n", + EnumValues: []EnumValue{ + {Value: "\"FullDocumentation\""}, + {Value: "\"NoDocumentation\""}, + {Value: "\"SingleLine\""}, + { + Value: "\"Structured\"", + Doc: "`\"Structured\"` is an experimental setting that returns a structured hover format.\nThis format separates the signature from the documentation, so that the client\ncan do more manipulation of these fields.\n\nThis should only be used by clients that support this behavior.\n", + }, + {Value: "\"SynopsisDocumentation\""}, + }, + Default: "\"FullDocumentation\"", + Hierarchy: "ui.documentation", + }, + { + Name: "linkTarget", + Type: "string", + Doc: "linkTarget controls where documentation links go.\nIt might be one of:\n\n* `\"godoc.org\"`\n* `\"pkg.go.dev\"`\n\nIf company chooses to use its own `godoc.org`, its address can be used as well.\n\nModules matching the GOPRIVATE environment variable will not have\ndocumentation links in hover.\n", + Default: "\"pkg.go.dev\"", + Hierarchy: "ui.documentation", + }, + { + Name: "linksInHover", + Type: "bool", + Doc: "linksInHover toggles the presence of links to documentation in hover.\n", + Default: "true", + Hierarchy: "ui.documentation", + }, + { + Name: "usePlaceholders", + Type: "bool", + Doc: "placeholders enables placeholders for function parameters or struct\nfields in completion responses.\n", + Default: "false", + Hierarchy: "ui.completion", + }, + { + Name: "completionBudget", + Type: "time.Duration", + Doc: "completionBudget is the soft latency goal for completion requests. Most\nrequests finish in a couple milliseconds, but in some cases deep\ncompletions can take much longer. As we use up our budget we\ndynamically reduce the search scope to ensure we return timely\nresults. Zero means unlimited.\n", + Default: "\"100ms\"", + Status: "debug", + Hierarchy: "ui.completion", + }, + { + Name: "matcher", + Type: "enum", + Doc: "matcher sets the algorithm that is used when calculating completion\ncandidates.\n", + EnumValues: []EnumValue{ + {Value: "\"CaseInsensitive\""}, + {Value: "\"CaseSensitive\""}, + {Value: "\"Fuzzy\""}, + }, + Default: "\"Fuzzy\"", + Status: "advanced", + Hierarchy: "ui.completion", + }, + { + Name: "experimentalPostfixCompletions", + Type: "bool", + Doc: "experimentalPostfixCompletions enables artificial method snippets\nsuch as \"someSlice.sort!\".\n", + Default: "true", + Status: "experimental", + Hierarchy: "ui.completion", + }, + { + Name: "importShortcut", + Type: "enum", + Doc: "importShortcut specifies whether import statements should link to\ndocumentation or go to definitions.\n", + EnumValues: []EnumValue{ + {Value: "\"Both\""}, + {Value: "\"Definition\""}, + {Value: "\"Link\""}, + }, + Default: "\"Both\"", + Hierarchy: "ui.navigation", + }, + { + Name: "symbolMatcher", + Type: "enum", + Doc: "symbolMatcher sets the algorithm that is used when finding workspace symbols.\n", + EnumValues: []EnumValue{ + {Value: "\"CaseInsensitive\""}, + {Value: "\"CaseSensitive\""}, + {Value: "\"FastFuzzy\""}, + {Value: "\"Fuzzy\""}, + }, + Default: "\"FastFuzzy\"", + Status: "advanced", + Hierarchy: "ui.navigation", + }, + { + Name: "symbolStyle", + Type: "enum", + Doc: "symbolStyle controls how symbols are qualified in symbol responses.\n\nExample Usage:\n\n```json5\n\"gopls\": {\n...\n \"symbolStyle\": \"Dynamic\",\n...\n}\n```\n", + EnumValues: []EnumValue{ + { + Value: "\"Dynamic\"", + Doc: "`\"Dynamic\"` uses whichever qualifier results in the highest scoring\nmatch for the given symbol query. Here a \"qualifier\" is any \"/\" or \".\"\ndelimited suffix of the fully qualified symbol. i.e. \"to/pkg.Foo.Field\" or\njust \"Foo.Field\".\n", + }, + { + Value: "\"Full\"", + Doc: "`\"Full\"` is fully qualified symbols, i.e.\n\"path/to/pkg.Foo.Field\".\n", + }, + { + Value: "\"Package\"", + Doc: "`\"Package\"` is package qualified symbols i.e.\n\"pkg.Foo.Field\".\n", + }, + }, + Default: "\"Dynamic\"", + Status: "advanced", + Hierarchy: "ui.navigation", + }, + { + Name: "analyses", + Type: "map[string]bool", + Doc: "analyses specify analyses that the user would like to enable or disable.\nA map of the names of analysis passes that should be enabled/disabled.\nA full list of analyzers that gopls uses can be found in\n[analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md).\n\nExample Usage:\n\n```json5\n...\n\"analyses\": {\n \"unreachable\": false, // Disable the unreachable analyzer.\n \"unusedparams\": true // Enable the unusedparams analyzer.\n}\n...\n```\n", + EnumKeys: EnumKeys{ + ValueType: "bool", + Keys: []EnumKey{ + { + Name: "\"asmdecl\"", + Doc: "report mismatches between assembly files and Go declarations", + Default: "true", + }, + { + Name: "\"assign\"", + Doc: "check for useless assignments\n\nThis checker reports assignments of the form x = x or a[i] = a[i].\nThese are almost always useless, and even when they aren't they are\nusually a mistake.", + Default: "true", + }, + { + Name: "\"atomic\"", + Doc: "check for common mistakes using the sync/atomic package\n\nThe atomic checker looks for assignment statements of the form:\n\n\tx = atomic.AddUint64(&x, 1)\n\nwhich are not atomic.", + Default: "true", + }, + { + Name: "\"atomicalign\"", + Doc: "check for non-64-bits-aligned arguments to sync/atomic functions", + Default: "true", + }, + { + Name: "\"bools\"", + Doc: "check for common mistakes involving boolean operators", + Default: "true", + }, + { + Name: "\"buildtag\"", + Doc: "check that +build tags are well-formed and correctly located", + Default: "true", + }, + { + Name: "\"cgocall\"", + Doc: "detect some violations of the cgo pointer passing rules\n\nCheck for invalid cgo pointer passing.\nThis looks for code that uses cgo to call C code passing values\nwhose types are almost always invalid according to the cgo pointer\nsharing rules.\nSpecifically, it warns about attempts to pass a Go chan, map, func,\nor slice to C, either directly, or via a pointer, array, or struct.", + Default: "true", + }, + { + Name: "\"composites\"", + Doc: "check for unkeyed composite literals\n\nThis analyzer reports a diagnostic for composite literals of struct\ntypes imported from another package that do not use the field-keyed\nsyntax. Such literals are fragile because the addition of a new field\n(even if unexported) to the struct will cause compilation to fail.\n\nAs an example,\n\n\terr = &net.DNSConfigError{err}\n\nshould be replaced by:\n\n\terr = &net.DNSConfigError{Err: err}\n", + Default: "true", + }, + { + Name: "\"copylocks\"", + Doc: "check for locks erroneously passed by value\n\nInadvertently copying a value containing a lock, such as sync.Mutex or\nsync.WaitGroup, may cause both copies to malfunction. Generally such\nvalues should be referred to through a pointer.", + Default: "true", + }, + { + Name: "\"deepequalerrors\"", + Doc: "check for calls of reflect.DeepEqual on error values\n\nThe deepequalerrors checker looks for calls of the form:\n\n reflect.DeepEqual(err1, err2)\n\nwhere err1 and err2 are errors. Using reflect.DeepEqual to compare\nerrors is discouraged.", + Default: "true", + }, + { + Name: "\"embed\"", + Doc: "check for //go:embed directive import\n\nThis analyzer checks that the embed package is imported when source code contains //go:embed comment directives.\nThe embed package must be imported for //go:embed directives to function.import _ \"embed\".", + Default: "true", + }, + { + Name: "\"errorsas\"", + Doc: "report passing non-pointer or non-error values to errors.As\n\nThe errorsas analysis reports calls to errors.As where the type\nof the second argument is not a pointer to a type implementing error.", + Default: "true", + }, + { + Name: "\"fieldalignment\"", + Doc: "find structs that would use less memory if their fields were sorted\n\nThis analyzer find structs that can be rearranged to use less memory, and provides\na suggested edit with the most compact order.\n\nNote that there are two different diagnostics reported. One checks struct size,\nand the other reports \"pointer bytes\" used. Pointer bytes is how many bytes of the\nobject that the garbage collector has to potentially scan for pointers, for example:\n\n\tstruct { uint32; string }\n\nhave 16 pointer bytes because the garbage collector has to scan up through the string's\ninner pointer.\n\n\tstruct { string; *uint32 }\n\nhas 24 pointer bytes because it has to scan further through the *uint32.\n\n\tstruct { string; uint32 }\n\nhas 8 because it can stop immediately after the string pointer.\n\nBe aware that the most compact order is not always the most efficient.\nIn rare cases it may cause two variables each updated by its own goroutine\nto occupy the same CPU cache line, inducing a form of memory contention\nknown as \"false sharing\" that slows down both goroutines.\n", + Default: "false", + }, + { + Name: "\"httpresponse\"", + Doc: "check for mistakes using HTTP responses\n\nA common mistake when using the net/http package is to defer a function\ncall to close the http.Response Body before checking the error that\ndetermines whether the response is valid:\n\n\tresp, err := http.Head(url)\n\tdefer resp.Body.Close()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t// (defer statement belongs here)\n\nThis checker helps uncover latent nil dereference bugs by reporting a\ndiagnostic for such mistakes.", + Default: "true", + }, + { + Name: "\"ifaceassert\"", + Doc: "detect impossible interface-to-interface type assertions\n\nThis checker flags type assertions v.(T) and corresponding type-switch cases\nin which the static type V of v is an interface that cannot possibly implement\nthe target interface T. This occurs when V and T contain methods with the same\nname but different signatures. Example:\n\n\tvar v interface {\n\t\tRead()\n\t}\n\t_ = v.(io.Reader)\n\nThe Read method in v has a different signature than the Read method in\nio.Reader, so this assertion cannot succeed.\n", + Default: "true", + }, + { + Name: "\"infertypeargs\"", + Doc: "check for unnecessary type arguments in call expressions\n\nExplicit type arguments may be omitted from call expressions if they can be\ninferred from function arguments, or from other type arguments:\n\n\tfunc f[T any](T) {}\n\t\n\tfunc _() {\n\t\tf[string](\"foo\") // string could be inferred\n\t}\n", + Default: "true", + }, + { + Name: "\"loopclosure\"", + Doc: "check references to loop variables from within nested functions\n\nThis analyzer checks for references to loop variables from within a function\nliteral inside the loop body. It checks for patterns where access to a loop\nvariable is known to escape the current loop iteration:\n 1. a call to go or defer at the end of the loop body\n 2. a call to golang.org/x/sync/errgroup.Group.Go at the end of the loop body\n 3. a call testing.T.Run where the subtest body invokes t.Parallel()\n\nIn the case of (1) and (2), the analyzer only considers references in the last\nstatement of the loop body as it is not deep enough to understand the effects\nof subsequent statements which might render the reference benign.\n\nFor example:\n\n\tfor i, v := range s {\n\t\tgo func() {\n\t\t\tprintln(i, v) // not what you might expect\n\t\t}()\n\t}\n\nSee: https://golang.org/doc/go_faq.html#closures_and_goroutines", + Default: "true", + }, + { + Name: "\"lostcancel\"", + Doc: "check cancel func returned by context.WithCancel is called\n\nThe cancellation function returned by context.WithCancel, WithTimeout,\nand WithDeadline must be called or the new context will remain live\nuntil its parent context is cancelled.\n(The background context is never cancelled.)", + Default: "true", + }, + { + Name: "\"nilfunc\"", + Doc: "check for useless comparisons between functions and nil\n\nA useless comparison is one like f == nil as opposed to f() == nil.", + Default: "true", + }, + { + Name: "\"nilness\"", + Doc: "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}\n", + Default: "false", + }, + { + Name: "\"printf\"", + Doc: "check consistency of Printf format strings and arguments\n\nThe check applies to known functions (for example, those in package fmt)\nas well as any detected wrappers of known functions.\n\nA function that wants to avail itself of printf checking but is not\nfound by this analyzer's heuristics (for example, due to use of\ndynamic calls) can insert a bogus call:\n\n\tif false {\n\t\t_ = fmt.Sprintf(format, args...) // enable printf checking\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.\n", + Default: "true", + }, + { + Name: "\"shadow\"", + Doc: "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}\n", + Default: "false", + }, + { + Name: "\"shift\"", + Doc: "check for shifts that equal or exceed the width of the integer", + Default: "true", + }, + { + Name: "\"simplifycompositelit\"", + Doc: "check for composite literal simplifications\n\nAn array, slice, or map composite literal of the form:\n\t[]T{T{}, T{}}\nwill be simplified to:\n\t[]T{{}, {}}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + Default: "true", + }, + { + Name: "\"simplifyrange\"", + Doc: "check for range statement simplifications\n\nA range of the form:\n\tfor x, _ = range v {...}\nwill be simplified to:\n\tfor x = range v {...}\n\nA range of the form:\n\tfor _ = range v {...}\nwill be simplified to:\n\tfor range v {...}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + Default: "true", + }, + { + Name: "\"simplifyslice\"", + Doc: "check for slice simplifications\n\nA slice expression of the form:\n\ts[a:len(s)]\nwill be simplified to:\n\ts[a:]\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + Default: "true", + }, + { + Name: "\"sortslice\"", + Doc: "check the argument type of sort.Slice\n\nsort.Slice requires an argument of a slice type. Check that\nthe interface{} value passed to sort.Slice is actually a slice.", + Default: "true", + }, + { + Name: "\"stdmethods\"", + Doc: "check signature of methods of well-known interfaces\n\nSometimes a type may be intended to satisfy an interface but may fail to\ndo so because of a mistake in its method signature.\nFor example, the result of this WriteTo method should be (int64, error),\nnot error, to satisfy io.WriterTo:\n\n\ttype myWriterTo struct{...}\n func (myWriterTo) WriteTo(w io.Writer) error { ... }\n\nThis check ensures that each method whose name matches one of several\nwell-known interface methods from the standard library has the correct\nsignature for that interface.\n\nChecked method names include:\n\tFormat GobEncode GobDecode MarshalJSON MarshalXML\n\tPeek ReadByte ReadFrom ReadRune Scan Seek\n\tUnmarshalJSON UnreadByte UnreadRune WriteByte\n\tWriteTo\n", + Default: "true", + }, + { + Name: "\"stringintconv\"", + Doc: "check for string(int) conversions\n\nThis checker flags conversions of the form string(x) where x is an integer\n(but not byte or rune) type. Such conversions are discouraged because they\nreturn the UTF-8 representation of the Unicode code point x, and not a decimal\nstring representation of x as one might expect. Furthermore, if x denotes an\ninvalid code point, the conversion cannot be statically rejected.\n\nFor conversions that intend on using the code point, consider replacing them\nwith string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the\nstring representation of the value in the desired base.\n", + Default: "true", + }, + { + Name: "\"structtag\"", + Doc: "check that struct field tags conform to reflect.StructTag.Get\n\nAlso report certain struct tags (json, xml) used with unexported fields.", + Default: "true", + }, + { + Name: "\"testinggoroutine\"", + Doc: "report calls to (*testing.T).Fatal from goroutines started by a test.\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\nfunc TestFoo(t *testing.T) {\n go func() {\n t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n }()\n}\n", + Default: "true", + }, + { + Name: "\"tests\"", + Doc: "check for common mistaken usages of tests and examples\n\nThe tests checker walks Test, Benchmark and Example functions checking\nmalformed names, wrong signatures and examples documenting non-existent\nidentifiers.\n\nPlease see the documentation for package testing in golang.org/pkg/testing\nfor the conventions that are enforced for Tests, Benchmarks, and Examples.", + Default: "true", + }, + { + Name: "\"timeformat\"", + Doc: "check for calls of (time.Time).Format or time.Parse with 2006-02-01\n\nThe timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)\nformat. Internationally, \"yyyy-dd-mm\" does not occur in common calendar date\nstandards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.\n", + Default: "true", + }, + { + Name: "\"unmarshal\"", + Doc: "report passing non-pointer or non-interface values to unmarshal\n\nThe unmarshal analysis reports calls to functions such as json.Unmarshal\nin which the argument type is not a pointer or an interface.", + Default: "true", + }, + { + Name: "\"unreachable\"", + Doc: "check for unreachable code\n\nThe unreachable analyzer finds statements that execution can never reach\nbecause they are preceded by an return statement, a call to panic, an\ninfinite loop, or similar constructs.", + Default: "true", + }, + { + Name: "\"unsafeptr\"", + Doc: "check for invalid conversions of uintptr to unsafe.Pointer\n\nThe unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer\nto convert integers to pointers. A conversion from uintptr to\nunsafe.Pointer is invalid if it implies that there is a uintptr-typed\nword in memory that holds a pointer value, because that word will be\ninvisible to stack copying and to the garbage collector.", + Default: "true", + }, + { + Name: "\"unusedparams\"", + Doc: "check for unused parameters of functions\n\nThe unusedparams analyzer checks functions to see if there are\nany parameters that are not being used.\n\nTo reduce false positives it ignores:\n- methods\n- parameters that do not have a name or are underscored\n- functions in test files\n- functions with empty bodies or those with just a return stmt", + Default: "false", + }, + { + Name: "\"unusedresult\"", + Doc: "check for unused results of calls to some functions\n\nSome functions like fmt.Errorf return a result and have no side effects,\nso it is always a mistake to discard the result. This analyzer reports\ncalls to certain functions in which the result of the call is ignored.\n\nThe set of functions may be controlled using flags.", + Default: "true", + }, + { + Name: "\"unusedwrite\"", + Doc: "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\tfunc f(input []T) {\n\t\tfor i, v := range input { // v is a copy\n\t\t\tv.x = i // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\tfunc (t T) f() { // t is a copy\n\t\tt.x = i // unused write to field x\n\t}\n", + Default: "false", + }, + { + Name: "\"useany\"", + Doc: "check for constraints that could be simplified to \"any\"", + Default: "false", + }, + { + Name: "\"fillreturns\"", + Doc: "suggest fixes for errors due to an incorrect number of return values\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n", + Default: "true", + }, + { + Name: "\"nonewvars\"", + Doc: "suggested fixes for \"no new vars on left side of :=\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"no new vars on left side of :=\". For example:\n\tz := 1\n\tz := 2\nwill turn into\n\tz := 1\n\tz = 2\n", + Default: "true", + }, + { + Name: "\"noresultvalues\"", + Doc: "suggested fixes for unexpected return values\n\nThis checker provides suggested fixes for type errors of the\ntype \"no result values expected\" or \"too many return values\".\nFor example:\n\tfunc z() { return nil }\nwill turn into\n\tfunc z() { return }\n", + Default: "true", + }, + { + Name: "\"undeclaredname\"", + Doc: "suggested fixes for \"undeclared name: <>\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"undeclared name: <>\". It will either insert a new statement,\nsuch as:\n\n\"<> := \"\n\nor a new function declaration, such as:\n\nfunc <>(inferred parameters) {\n\tpanic(\"implement me!\")\n}\n", + Default: "true", + }, + { + Name: "\"unusedvariable\"", + Doc: "check for unused variables\n\nThe unusedvariable analyzer suggests fixes for unused variables errors.\n", + Default: "false", + }, + { + Name: "\"fillstruct\"", + Doc: "note incomplete struct initializations\n\nThis analyzer provides diagnostics for any struct literals that do not have\nany fields initialized. Because the suggested fix for this analysis is\nexpensive to compute, callers should compute it separately, using the\nSuggestedFix function below.\n", + Default: "true", + }, + { + Name: "\"stubmethods\"", + Doc: "stub methods analyzer\n\nThis analyzer generates method stubs for concrete types\nin order to implement a target interface", + Default: "true", + }, + }, + }, + Default: "{}", + Hierarchy: "ui.diagnostic", + }, + { + Name: "staticcheck", + Type: "bool", + Doc: "staticcheck enables additional analyses from staticcheck.io.\nThese analyses are documented on\n[Staticcheck's website](https://staticcheck.io/docs/checks/).\n", + Default: "false", + Status: "experimental", + Hierarchy: "ui.diagnostic", + }, + { + Name: "annotations", + Type: "map[string]bool", + Doc: "annotations specifies the various kinds of optimization diagnostics\nthat should be reported by the gc_details command.\n", + EnumKeys: EnumKeys{ + ValueType: "bool", + Keys: []EnumKey{ + { + Name: "\"bounds\"", + Doc: "`\"bounds\"` controls bounds checking diagnostics.\n", + Default: "true", + }, + { + Name: "\"escape\"", + Doc: "`\"escape\"` controls diagnostics about escape choices.\n", + Default: "true", + }, + { + Name: "\"inline\"", + Doc: "`\"inline\"` controls diagnostics about inlining choices.\n", + Default: "true", + }, + { + Name: "\"nil\"", + Doc: "`\"nil\"` controls nil checks.\n", + Default: "true", + }, + }, + }, + Default: "{\"bounds\":true,\"escape\":true,\"inline\":true,\"nil\":true}", + Status: "experimental", + Hierarchy: "ui.diagnostic", + }, + { + Name: "diagnosticsDelay", + Type: "time.Duration", + Doc: "diagnosticsDelay controls the amount of time that gopls waits\nafter the most recent file modification before computing deep diagnostics.\nSimple diagnostics (parsing and type-checking) are always run immediately\non recently modified packages.\n\nThis option must be set to a valid duration string, for example `\"250ms\"`.\n", + Default: "\"250ms\"", + Status: "advanced", + Hierarchy: "ui.diagnostic", + }, + { + Name: "experimentalWatchedFileDelay", + Type: "time.Duration", + Doc: "experimentalWatchedFileDelay controls the amount of time that gopls waits\nfor additional workspace/didChangeWatchedFiles notifications to arrive,\nbefore processing all such notifications in a single batch. This is\nintended for use by LSP clients that don't support their own batching of\nfile system notifications.\n\nThis option must be set to a valid duration string, for example `\"100ms\"`.\n\nDeprecated: this setting is deprecated and will be removed in a future\nversion of gopls (https://go.dev/issue/55332)\n", + Default: "\"0s\"", + Status: "experimental", + Hierarchy: "ui.diagnostic", + }, + { + Name: "hints", + Type: "map[string]bool", + Doc: "hints specify inlay hints that users want to see. A full list of hints\nthat gopls uses can be found in\n[inlayHints.md](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md).\n", + EnumKeys: EnumKeys{Keys: []EnumKey{ + { + Name: "\"assignVariableTypes\"", + Doc: "Enable/disable inlay hints for variable types in assign statements:\n```go\n\ti/* int*/, j/* int*/ := 0, len(r)-1\n```", + Default: "false", + }, + { + Name: "\"compositeLiteralFields\"", + Doc: "Enable/disable inlay hints for composite literal field names:\n```go\n\t{/*in: */\"Hello, world\", /*want: */\"dlrow ,olleH\"}\n```", + Default: "false", + }, + { + Name: "\"compositeLiteralTypes\"", + Doc: "Enable/disable inlay hints for composite literal types:\n```go\n\tfor _, c := range []struct {\n\t\tin, want string\n\t}{\n\t\t/*struct{ in string; want string }*/{\"Hello, world\", \"dlrow ,olleH\"},\n\t}\n```", + Default: "false", + }, + { + Name: "\"constantValues\"", + Doc: "Enable/disable inlay hints for constant values:\n```go\n\tconst (\n\t\tKindNone Kind = iota/* = 0*/\n\t\tKindPrint/* = 1*/\n\t\tKindPrintf/* = 2*/\n\t\tKindErrorf/* = 3*/\n\t)\n```", + Default: "false", + }, + { + Name: "\"functionTypeParameters\"", + Doc: "Enable/disable inlay hints for implicit type parameters on generic functions:\n```go\n\tmyFoo/*[int, string]*/(1, \"hello\")\n```", + Default: "false", + }, + { + Name: "\"parameterNames\"", + Doc: "Enable/disable inlay hints for parameter names:\n```go\n\tparseInt(/* str: */ \"123\", /* radix: */ 8)\n```", + Default: "false", + }, + { + Name: "\"rangeVariableTypes\"", + Doc: "Enable/disable inlay hints for variable types in range statements:\n```go\n\tfor k/* int*/, v/* string*/ := range []string{} {\n\t\tfmt.Println(k, v)\n\t}\n```", + Default: "false", + }, + }}, + Default: "{}", + Status: "experimental", + Hierarchy: "ui.inlayhint", + }, + { + Name: "codelenses", + Type: "map[string]bool", + Doc: "codelenses overrides the enabled/disabled state of code lenses. See the\n\"Code Lenses\" section of the\n[Settings page](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#code-lenses)\nfor the list of supported lenses.\n\nExample Usage:\n\n```json5\n\"gopls\": {\n...\n \"codelenses\": {\n \"generate\": false, // Don't show the `go generate` lens.\n \"gc_details\": true // Show a code lens toggling the display of gc's choices.\n }\n...\n}\n```\n", + EnumKeys: EnumKeys{ + ValueType: "bool", + Keys: []EnumKey{ + { + Name: "\"gc_details\"", + Doc: "Toggle the calculation of gc annotations.", + Default: "false", + }, + { + Name: "\"generate\"", + Doc: "Runs `go generate` for a given directory.", + Default: "true", + }, + { + Name: "\"regenerate_cgo\"", + Doc: "Regenerates cgo definitions.", + Default: "true", + }, + { + Name: "\"run_vulncheck_exp\"", + Doc: "Run vulnerability check (`govulncheck`).", + Default: "false", + }, + { + Name: "\"test\"", + Doc: "Runs `go test` for a specific set of test or benchmark functions.", + Default: "false", + }, + { + Name: "\"tidy\"", + Doc: "Runs `go mod tidy` for a module.", + Default: "true", + }, + { + Name: "\"upgrade_dependency\"", + Doc: "Upgrades a dependency in the go.mod file for a module.", + Default: "true", + }, + { + Name: "\"vendor\"", + Doc: "Runs `go mod vendor` for a module.", + Default: "true", + }, + }, + }, + Default: "{\"gc_details\":false,\"generate\":true,\"regenerate_cgo\":true,\"tidy\":true,\"upgrade_dependency\":true,\"vendor\":true}", + Hierarchy: "ui", + }, + { + Name: "semanticTokens", + Type: "bool", + Doc: "semanticTokens controls whether the LSP server will send\nsemantic tokens to the client.\n", + Default: "false", + Status: "experimental", + Hierarchy: "ui", + }, + { + Name: "noSemanticString", + Type: "bool", + Doc: "noSemanticString turns off the sending of the semantic token 'string'\n", + Default: "false", + Status: "experimental", + Hierarchy: "ui", + }, + { + Name: "noSemanticNumber", + Type: "bool", + Doc: "noSemanticNumber turns off the sending of the semantic token 'number'\n", + Default: "false", + Status: "experimental", + Hierarchy: "ui", + }, + { + Name: "local", + Type: "string", + Doc: "local is the equivalent of the `goimports -local` flag, which puts\nimports beginning with this string after third-party packages. It should\nbe the prefix of the import path whose imports should be grouped\nseparately.\n", + Default: "\"\"", + Hierarchy: "formatting", + }, + { + Name: "gofumpt", + Type: "bool", + Doc: "gofumpt indicates if we should run gofumpt formatting.\n", + Default: "false", + Hierarchy: "formatting", + }, + { + Name: "verboseOutput", + Type: "bool", + Doc: "verboseOutput enables additional debug logging.\n", + Default: "false", + Status: "debug", + }, + }, + }, + Commands: []*CommandJSON{ + { + Command: "gopls.add_dependency", + Title: "Add a dependency", + Doc: "Adds a dependency to the go.mod file for a module.", + ArgDoc: "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// Additional args to pass to the go command.\n\t\"GoCmdArgs\": []string,\n\t// Whether to add a require directive.\n\t\"AddRequire\": bool,\n}", + }, + { + Command: "gopls.add_import", + Title: "Add an import", + Doc: "Ask the server to add an import path to a given Go file. The method will\ncall applyEdit on the client so that clients don't have to apply the edit\nthemselves.", + ArgDoc: "{\n\t// ImportPath is the target import path that should\n\t// be added to the URI file\n\t\"ImportPath\": string,\n\t// URI is the file that the ImportPath should be\n\t// added to\n\t\"URI\": string,\n}", + }, + { + Command: "gopls.apply_fix", + Title: "Apply a fix", + Doc: "Applies a fix to a region of source code.", + ArgDoc: "{\n\t// The fix to apply.\n\t\"Fix\": string,\n\t// The file URI for the document to fix.\n\t\"URI\": string,\n\t// The document range to scan for fixes.\n\t\"Range\": {\n\t\t\"start\": {\n\t\t\t\"line\": uint32,\n\t\t\t\"character\": uint32,\n\t\t},\n\t\t\"end\": {\n\t\t\t\"line\": uint32,\n\t\t\t\"character\": uint32,\n\t\t},\n\t},\n}", + }, + { + Command: "gopls.check_upgrades", + Title: "Check for upgrades", + Doc: "Checks for module upgrades.", + ArgDoc: "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// The modules to check.\n\t\"Modules\": []string,\n}", + }, + { + Command: "gopls.edit_go_directive", + Title: "Run go mod edit -go=version", + Doc: "Runs `go mod edit -go=version` for a module.", + ArgDoc: "{\n\t// Any document URI within the relevant module.\n\t\"URI\": string,\n\t// The version to pass to `go mod edit -go`.\n\t\"Version\": string,\n}", + }, + { + Command: "gopls.gc_details", + Title: "Toggle gc_details", + Doc: "Toggle the calculation of gc annotations.", + ArgDoc: "string", + }, + { + Command: "gopls.generate", + Title: "Run go generate", + Doc: "Runs `go generate` for a given directory.", + ArgDoc: "{\n\t// URI for the directory to generate.\n\t\"Dir\": string,\n\t// Whether to generate recursively (go generate ./...)\n\t\"Recursive\": bool,\n}", + }, + { + Command: "gopls.generate_gopls_mod", + Title: "Generate gopls.mod", + Doc: "(Re)generate the gopls.mod file for a workspace.", + ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", + }, + { + Command: "gopls.go_get_package", + Title: "go get a package", + Doc: "Runs `go get` to fetch a package.", + ArgDoc: "{\n\t// Any document URI within the relevant module.\n\t\"URI\": string,\n\t// The package to go get.\n\t\"Pkg\": string,\n\t\"AddRequire\": bool,\n}", + }, + { + Command: "gopls.list_imports", + Title: "List imports of a file and its package", + Doc: "Retrieve a list of imports in the given Go file, and the package it\nbelongs to.", + ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", + ResultDoc: "{\n\t// Imports is a list of imports in the requested file.\n\t\"Imports\": []{\n\t\t\"Path\": string,\n\t\t\"Name\": string,\n\t},\n\t// PackageImports is a list of all imports in the requested file's package.\n\t\"PackageImports\": []{\n\t\t\"Path\": string,\n\t},\n}", + }, + { + Command: "gopls.list_known_packages", + Title: "List known packages", + Doc: "Retrieve a list of packages that are importable from the given URI.", + ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", + ResultDoc: "{\n\t// Packages is a list of packages relative\n\t// to the URIArg passed by the command request.\n\t// In other words, it omits paths that are already\n\t// imported or cannot be imported due to compiler\n\t// restrictions.\n\t\"Packages\": []string,\n}", + }, + { + Command: "gopls.regenerate_cgo", + Title: "Regenerate cgo", + Doc: "Regenerates cgo definitions.", + ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", + }, + { + Command: "gopls.remove_dependency", + Title: "Remove a dependency", + Doc: "Removes a dependency from the go.mod file of a module.", + ArgDoc: "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// The module path to remove.\n\t\"ModulePath\": string,\n\t\"OnlyDiagnostic\": bool,\n}", + }, + { + Command: "gopls.reset_go_mod_diagnostics", + Title: "Reset go.mod diagnostics", + Doc: "Reset diagnostics in the go.mod file of a module.", + ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", + }, + { + Command: "gopls.run_tests", + Title: "Run test(s)", + Doc: "Runs `go test` for a specific set of test or benchmark functions.", + ArgDoc: "{\n\t// The test file containing the tests to run.\n\t\"URI\": string,\n\t// Specific test names to run, e.g. TestFoo.\n\t\"Tests\": []string,\n\t// Specific benchmarks to run, e.g. BenchmarkFoo.\n\t\"Benchmarks\": []string,\n}", + }, + { + Command: "gopls.run_vulncheck_exp", + Title: "Run vulncheck (experimental)", + Doc: "Run vulnerability check (`govulncheck`).", + ArgDoc: "{\n\t// Any document in the directory from which govulncheck will run.\n\t\"URI\": string,\n\t// Package pattern. E.g. \"\", \".\", \"./...\".\n\t\"Pattern\": string,\n}", + }, + { + Command: "gopls.start_debugging", + Title: "Start the gopls debug server", + Doc: "Start the gopls debug server if it isn't running, and return the debug\naddress.", + ArgDoc: "{\n\t// Optional: the address (including port) for the debug server to listen on.\n\t// If not provided, the debug server will bind to \"localhost:0\", and the\n\t// full debug URL will be contained in the result.\n\t// \n\t// If there is more than one gopls instance along the serving path (i.e. you\n\t// are using a daemon), each gopls instance will attempt to start debugging.\n\t// If Addr specifies a port, only the daemon will be able to bind to that\n\t// port, and each intermediate gopls instance will fail to start debugging.\n\t// For this reason it is recommended not to specify a port (or equivalently,\n\t// to specify \":0\").\n\t// \n\t// If the server was already debugging this field has no effect, and the\n\t// result will contain the previously configured debug URL(s).\n\t\"Addr\": string,\n}", + ResultDoc: "{\n\t// The URLs to use to access the debug servers, for all gopls instances in\n\t// the serving path. For the common case of a single gopls instance (i.e. no\n\t// daemon), this will be exactly one address.\n\t// \n\t// In the case of one or more gopls instances forwarding the LSP to a daemon,\n\t// URLs will contain debug addresses for each server in the serving path, in\n\t// serving order. The daemon debug address will be the last entry in the\n\t// slice. If any intermediate gopls instance fails to start debugging, no\n\t// error will be returned but the debug URL for that server in the URLs slice\n\t// will be empty.\n\t\"URLs\": []string,\n}", + }, + { + Command: "gopls.test", + Title: "Run test(s) (legacy)", + Doc: "Runs `go test` for a specific set of test or benchmark functions.", + ArgDoc: "string,\n[]string,\n[]string", + }, + { + Command: "gopls.tidy", + Title: "Run go mod tidy", + Doc: "Runs `go mod tidy` for a module.", + ArgDoc: "{\n\t// The file URIs.\n\t\"URIs\": []string,\n}", + }, + { + Command: "gopls.toggle_gc_details", + Title: "Toggle gc_details", + Doc: "Toggle the calculation of gc annotations.", + ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", + }, + { + Command: "gopls.update_go_sum", + Title: "Update go.sum", + Doc: "Updates the go.sum file for a module.", + ArgDoc: "{\n\t// The file URIs.\n\t\"URIs\": []string,\n}", + }, + { + Command: "gopls.upgrade_dependency", + Title: "Upgrade a dependency", + Doc: "Upgrades a dependency in the go.mod file for a module.", + ArgDoc: "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// Additional args to pass to the go command.\n\t\"GoCmdArgs\": []string,\n\t// Whether to add a require directive.\n\t\"AddRequire\": bool,\n}", + }, + { + Command: "gopls.vendor", + Title: "Run go mod vendor", + Doc: "Runs `go mod vendor` for a module.", + ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", + }, + }, + Lenses: []*LensJSON{ + { + Lens: "gc_details", + Title: "Toggle gc_details", + Doc: "Toggle the calculation of gc annotations.", + }, + { + Lens: "generate", + Title: "Run go generate", + Doc: "Runs `go generate` for a given directory.", + }, + { + Lens: "regenerate_cgo", + Title: "Regenerate cgo", + Doc: "Regenerates cgo definitions.", + }, + { + Lens: "run_vulncheck_exp", + Title: "Run vulncheck (experimental)", + Doc: "Run vulnerability check (`govulncheck`).", + }, + { + Lens: "test", + Title: "Run test(s) (legacy)", + Doc: "Runs `go test` for a specific set of test or benchmark functions.", + }, + { + Lens: "tidy", + Title: "Run go mod tidy", + Doc: "Runs `go mod tidy` for a module.", + }, + { + Lens: "upgrade_dependency", + Title: "Upgrade a dependency", + Doc: "Upgrades a dependency in the go.mod file for a module.", + }, + { + Lens: "vendor", + Title: "Run go mod vendor", + Doc: "Runs `go mod vendor` for a module.", + }, + }, + Analyzers: []*AnalyzerJSON{ + { + Name: "asmdecl", + Doc: "report mismatches between assembly files and Go declarations", + Default: true, + }, + { + Name: "assign", + Doc: "check for useless assignments\n\nThis checker reports assignments of the form x = x or a[i] = a[i].\nThese are almost always useless, and even when they aren't they are\nusually a mistake.", + Default: true, + }, + { + Name: "atomic", + Doc: "check for common mistakes using the sync/atomic package\n\nThe atomic checker looks for assignment statements of the form:\n\n\tx = atomic.AddUint64(&x, 1)\n\nwhich are not atomic.", + Default: true, + }, + { + Name: "atomicalign", + Doc: "check for non-64-bits-aligned arguments to sync/atomic functions", + Default: true, + }, + { + Name: "bools", + Doc: "check for common mistakes involving boolean operators", + Default: true, + }, + { + Name: "buildtag", + Doc: "check that +build tags are well-formed and correctly located", + Default: true, + }, + { + Name: "cgocall", + Doc: "detect some violations of the cgo pointer passing rules\n\nCheck for invalid cgo pointer passing.\nThis looks for code that uses cgo to call C code passing values\nwhose types are almost always invalid according to the cgo pointer\nsharing rules.\nSpecifically, it warns about attempts to pass a Go chan, map, func,\nor slice to C, either directly, or via a pointer, array, or struct.", + Default: true, + }, + { + Name: "composites", + Doc: "check for unkeyed composite literals\n\nThis analyzer reports a diagnostic for composite literals of struct\ntypes imported from another package that do not use the field-keyed\nsyntax. Such literals are fragile because the addition of a new field\n(even if unexported) to the struct will cause compilation to fail.\n\nAs an example,\n\n\terr = &net.DNSConfigError{err}\n\nshould be replaced by:\n\n\terr = &net.DNSConfigError{Err: err}\n", + Default: true, + }, + { + Name: "copylocks", + Doc: "check for locks erroneously passed by value\n\nInadvertently copying a value containing a lock, such as sync.Mutex or\nsync.WaitGroup, may cause both copies to malfunction. Generally such\nvalues should be referred to through a pointer.", + Default: true, + }, + { + Name: "deepequalerrors", + Doc: "check for calls of reflect.DeepEqual on error values\n\nThe deepequalerrors checker looks for calls of the form:\n\n reflect.DeepEqual(err1, err2)\n\nwhere err1 and err2 are errors. Using reflect.DeepEqual to compare\nerrors is discouraged.", + Default: true, + }, + { + Name: "embed", + Doc: "check for //go:embed directive import\n\nThis analyzer checks that the embed package is imported when source code contains //go:embed comment directives.\nThe embed package must be imported for //go:embed directives to function.import _ \"embed\".", + Default: true, + }, + { + Name: "errorsas", + Doc: "report passing non-pointer or non-error values to errors.As\n\nThe errorsas analysis reports calls to errors.As where the type\nof the second argument is not a pointer to a type implementing error.", + Default: true, + }, + { + Name: "fieldalignment", + Doc: "find structs that would use less memory if their fields were sorted\n\nThis analyzer find structs that can be rearranged to use less memory, and provides\na suggested edit with the most compact order.\n\nNote that there are two different diagnostics reported. One checks struct size,\nand the other reports \"pointer bytes\" used. Pointer bytes is how many bytes of the\nobject that the garbage collector has to potentially scan for pointers, for example:\n\n\tstruct { uint32; string }\n\nhave 16 pointer bytes because the garbage collector has to scan up through the string's\ninner pointer.\n\n\tstruct { string; *uint32 }\n\nhas 24 pointer bytes because it has to scan further through the *uint32.\n\n\tstruct { string; uint32 }\n\nhas 8 because it can stop immediately after the string pointer.\n\nBe aware that the most compact order is not always the most efficient.\nIn rare cases it may cause two variables each updated by its own goroutine\nto occupy the same CPU cache line, inducing a form of memory contention\nknown as \"false sharing\" that slows down both goroutines.\n", + }, + { + Name: "httpresponse", + Doc: "check for mistakes using HTTP responses\n\nA common mistake when using the net/http package is to defer a function\ncall to close the http.Response Body before checking the error that\ndetermines whether the response is valid:\n\n\tresp, err := http.Head(url)\n\tdefer resp.Body.Close()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t// (defer statement belongs here)\n\nThis checker helps uncover latent nil dereference bugs by reporting a\ndiagnostic for such mistakes.", + Default: true, + }, + { + Name: "ifaceassert", + Doc: "detect impossible interface-to-interface type assertions\n\nThis checker flags type assertions v.(T) and corresponding type-switch cases\nin which the static type V of v is an interface that cannot possibly implement\nthe target interface T. This occurs when V and T contain methods with the same\nname but different signatures. Example:\n\n\tvar v interface {\n\t\tRead()\n\t}\n\t_ = v.(io.Reader)\n\nThe Read method in v has a different signature than the Read method in\nio.Reader, so this assertion cannot succeed.\n", + Default: true, + }, + { + Name: "infertypeargs", + Doc: "check for unnecessary type arguments in call expressions\n\nExplicit type arguments may be omitted from call expressions if they can be\ninferred from function arguments, or from other type arguments:\n\n\tfunc f[T any](T) {}\n\t\n\tfunc _() {\n\t\tf[string](\"foo\") // string could be inferred\n\t}\n", + Default: true, + }, + { + Name: "loopclosure", + Doc: "check references to loop variables from within nested functions\n\nThis analyzer checks for references to loop variables from within a function\nliteral inside the loop body. It checks for patterns where access to a loop\nvariable is known to escape the current loop iteration:\n 1. a call to go or defer at the end of the loop body\n 2. a call to golang.org/x/sync/errgroup.Group.Go at the end of the loop body\n 3. a call testing.T.Run where the subtest body invokes t.Parallel()\n\nIn the case of (1) and (2), the analyzer only considers references in the last\nstatement of the loop body as it is not deep enough to understand the effects\nof subsequent statements which might render the reference benign.\n\nFor example:\n\n\tfor i, v := range s {\n\t\tgo func() {\n\t\t\tprintln(i, v) // not what you might expect\n\t\t}()\n\t}\n\nSee: https://golang.org/doc/go_faq.html#closures_and_goroutines", + Default: true, + }, + { + Name: "lostcancel", + Doc: "check cancel func returned by context.WithCancel is called\n\nThe cancellation function returned by context.WithCancel, WithTimeout,\nand WithDeadline must be called or the new context will remain live\nuntil its parent context is cancelled.\n(The background context is never cancelled.)", + Default: true, + }, + { + Name: "nilfunc", + Doc: "check for useless comparisons between functions and nil\n\nA useless comparison is one like f == nil as opposed to f() == nil.", + Default: true, + }, + { + Name: "nilness", + Doc: "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}\n", + }, + { + Name: "printf", + Doc: "check consistency of Printf format strings and arguments\n\nThe check applies to known functions (for example, those in package fmt)\nas well as any detected wrappers of known functions.\n\nA function that wants to avail itself of printf checking but is not\nfound by this analyzer's heuristics (for example, due to use of\ndynamic calls) can insert a bogus call:\n\n\tif false {\n\t\t_ = fmt.Sprintf(format, args...) // enable printf checking\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.\n", + Default: true, + }, + { + Name: "shadow", + Doc: "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}\n", + }, + { + Name: "shift", + Doc: "check for shifts that equal or exceed the width of the integer", + Default: true, + }, + { + Name: "simplifycompositelit", + Doc: "check for composite literal simplifications\n\nAn array, slice, or map composite literal of the form:\n\t[]T{T{}, T{}}\nwill be simplified to:\n\t[]T{{}, {}}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + Default: true, + }, + { + Name: "simplifyrange", + Doc: "check for range statement simplifications\n\nA range of the form:\n\tfor x, _ = range v {...}\nwill be simplified to:\n\tfor x = range v {...}\n\nA range of the form:\n\tfor _ = range v {...}\nwill be simplified to:\n\tfor range v {...}\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + Default: true, + }, + { + Name: "simplifyslice", + Doc: "check for slice simplifications\n\nA slice expression of the form:\n\ts[a:len(s)]\nwill be simplified to:\n\ts[a:]\n\nThis is one of the simplifications that \"gofmt -s\" applies.", + Default: true, + }, + { + Name: "sortslice", + Doc: "check the argument type of sort.Slice\n\nsort.Slice requires an argument of a slice type. Check that\nthe interface{} value passed to sort.Slice is actually a slice.", + Default: true, + }, + { + Name: "stdmethods", + Doc: "check signature of methods of well-known interfaces\n\nSometimes a type may be intended to satisfy an interface but may fail to\ndo so because of a mistake in its method signature.\nFor example, the result of this WriteTo method should be (int64, error),\nnot error, to satisfy io.WriterTo:\n\n\ttype myWriterTo struct{...}\n func (myWriterTo) WriteTo(w io.Writer) error { ... }\n\nThis check ensures that each method whose name matches one of several\nwell-known interface methods from the standard library has the correct\nsignature for that interface.\n\nChecked method names include:\n\tFormat GobEncode GobDecode MarshalJSON MarshalXML\n\tPeek ReadByte ReadFrom ReadRune Scan Seek\n\tUnmarshalJSON UnreadByte UnreadRune WriteByte\n\tWriteTo\n", + Default: true, + }, + { + Name: "stringintconv", + Doc: "check for string(int) conversions\n\nThis checker flags conversions of the form string(x) where x is an integer\n(but not byte or rune) type. Such conversions are discouraged because they\nreturn the UTF-8 representation of the Unicode code point x, and not a decimal\nstring representation of x as one might expect. Furthermore, if x denotes an\ninvalid code point, the conversion cannot be statically rejected.\n\nFor conversions that intend on using the code point, consider replacing them\nwith string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the\nstring representation of the value in the desired base.\n", + Default: true, + }, + { + Name: "structtag", + Doc: "check that struct field tags conform to reflect.StructTag.Get\n\nAlso report certain struct tags (json, xml) used with unexported fields.", + Default: true, + }, + { + Name: "testinggoroutine", + Doc: "report calls to (*testing.T).Fatal from goroutines started by a test.\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\nfunc TestFoo(t *testing.T) {\n go func() {\n t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n }()\n}\n", + Default: true, + }, + { + Name: "tests", + Doc: "check for common mistaken usages of tests and examples\n\nThe tests checker walks Test, Benchmark and Example functions checking\nmalformed names, wrong signatures and examples documenting non-existent\nidentifiers.\n\nPlease see the documentation for package testing in golang.org/pkg/testing\nfor the conventions that are enforced for Tests, Benchmarks, and Examples.", + Default: true, + }, + { + Name: "timeformat", + Doc: "check for calls of (time.Time).Format or time.Parse with 2006-02-01\n\nThe timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)\nformat. Internationally, \"yyyy-dd-mm\" does not occur in common calendar date\nstandards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.\n", + Default: true, + }, + { + Name: "unmarshal", + Doc: "report passing non-pointer or non-interface values to unmarshal\n\nThe unmarshal analysis reports calls to functions such as json.Unmarshal\nin which the argument type is not a pointer or an interface.", + Default: true, + }, + { + Name: "unreachable", + Doc: "check for unreachable code\n\nThe unreachable analyzer finds statements that execution can never reach\nbecause they are preceded by an return statement, a call to panic, an\ninfinite loop, or similar constructs.", + Default: true, + }, + { + Name: "unsafeptr", + Doc: "check for invalid conversions of uintptr to unsafe.Pointer\n\nThe unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer\nto convert integers to pointers. A conversion from uintptr to\nunsafe.Pointer is invalid if it implies that there is a uintptr-typed\nword in memory that holds a pointer value, because that word will be\ninvisible to stack copying and to the garbage collector.", + Default: true, + }, + { + Name: "unusedparams", + Doc: "check for unused parameters of functions\n\nThe unusedparams analyzer checks functions to see if there are\nany parameters that are not being used.\n\nTo reduce false positives it ignores:\n- methods\n- parameters that do not have a name or are underscored\n- functions in test files\n- functions with empty bodies or those with just a return stmt", + }, + { + Name: "unusedresult", + Doc: "check for unused results of calls to some functions\n\nSome functions like fmt.Errorf return a result and have no side effects,\nso it is always a mistake to discard the result. This analyzer reports\ncalls to certain functions in which the result of the call is ignored.\n\nThe set of functions may be controlled using flags.", + Default: true, + }, + { + Name: "unusedwrite", + Doc: "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\tfunc f(input []T) {\n\t\tfor i, v := range input { // v is a copy\n\t\t\tv.x = i // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\tfunc (t T) f() { // t is a copy\n\t\tt.x = i // unused write to field x\n\t}\n", + }, + { + Name: "useany", + Doc: "check for constraints that could be simplified to \"any\"", + }, + { + Name: "fillreturns", + Doc: "suggest fixes for errors due to an incorrect number of return values\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n", + Default: true, + }, + { + Name: "nonewvars", + Doc: "suggested fixes for \"no new vars on left side of :=\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"no new vars on left side of :=\". For example:\n\tz := 1\n\tz := 2\nwill turn into\n\tz := 1\n\tz = 2\n", + Default: true, + }, + { + Name: "noresultvalues", + Doc: "suggested fixes for unexpected return values\n\nThis checker provides suggested fixes for type errors of the\ntype \"no result values expected\" or \"too many return values\".\nFor example:\n\tfunc z() { return nil }\nwill turn into\n\tfunc z() { return }\n", + Default: true, + }, + { + Name: "undeclaredname", + Doc: "suggested fixes for \"undeclared name: <>\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"undeclared name: <>\". It will either insert a new statement,\nsuch as:\n\n\"<> := \"\n\nor a new function declaration, such as:\n\nfunc <>(inferred parameters) {\n\tpanic(\"implement me!\")\n}\n", + Default: true, + }, + { + Name: "unusedvariable", + Doc: "check for unused variables\n\nThe unusedvariable analyzer suggests fixes for unused variables errors.\n", + }, + { + Name: "fillstruct", + Doc: "note incomplete struct initializations\n\nThis analyzer provides diagnostics for any struct literals that do not have\nany fields initialized. Because the suggested fix for this analysis is\nexpensive to compute, callers should compute it separately, using the\nSuggestedFix function below.\n", + Default: true, + }, + { + Name: "stubmethods", + Doc: "stub methods analyzer\n\nThis analyzer generates method stubs for concrete types\nin order to implement a target interface", + Default: true, + }, + }, + Hints: []*HintJSON{ + { + Name: "assignVariableTypes", + Doc: "Enable/disable inlay hints for variable types in assign statements:\n```go\n\ti/* int*/, j/* int*/ := 0, len(r)-1\n```", + }, + { + Name: "compositeLiteralFields", + Doc: "Enable/disable inlay hints for composite literal field names:\n```go\n\t{/*in: */\"Hello, world\", /*want: */\"dlrow ,olleH\"}\n```", + }, + { + Name: "compositeLiteralTypes", + Doc: "Enable/disable inlay hints for composite literal types:\n```go\n\tfor _, c := range []struct {\n\t\tin, want string\n\t}{\n\t\t/*struct{ in string; want string }*/{\"Hello, world\", \"dlrow ,olleH\"},\n\t}\n```", + }, + { + Name: "constantValues", + Doc: "Enable/disable inlay hints for constant values:\n```go\n\tconst (\n\t\tKindNone Kind = iota/* = 0*/\n\t\tKindPrint/* = 1*/\n\t\tKindPrintf/* = 2*/\n\t\tKindErrorf/* = 3*/\n\t)\n```", + }, + { + Name: "functionTypeParameters", + Doc: "Enable/disable inlay hints for implicit type parameters on generic functions:\n```go\n\tmyFoo/*[int, string]*/(1, \"hello\")\n```", + }, + { + Name: "parameterNames", + Doc: "Enable/disable inlay hints for parameter names:\n```go\n\tparseInt(/* str: */ \"123\", /* radix: */ 8)\n```", + }, + { + Name: "rangeVariableTypes", + Doc: "Enable/disable inlay hints for variable types in range statements:\n```go\n\tfor k/* int*/, v/* string*/ := range []string{} {\n\t\tfmt.Println(k, v)\n\t}\n```", + }, + }, +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/call_hierarchy.go b/gopls/golang_org_x_tools_gopls/lsp/source/call_hierarchy.go new file mode 100644 index 0000000..b2b98bb --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/call_hierarchy.go @@ -0,0 +1,318 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "errors" + "fmt" + "go/ast" + "go/token" + "go/types" + "path/filepath" + + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/tag" +) + +// PrepareCallHierarchy returns an array of CallHierarchyItem for a file and the position within the file. +func PrepareCallHierarchy(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyItem, error) { + ctx, done := event.Start(ctx, "source.PrepareCallHierarchy") + defer done() + + identifier, err := Identifier(ctx, snapshot, fh, pos) + if err != nil { + if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) { + return nil, nil + } + return nil, err + } + + // The identifier can be nil if it is an import spec. + if identifier == nil || identifier.Declaration.obj == nil { + return nil, nil + } + + if _, ok := identifier.Declaration.obj.Type().Underlying().(*types.Signature); !ok { + return nil, nil + } + + if len(identifier.Declaration.MappedRange) == 0 { + return nil, nil + } + declMappedRange := identifier.Declaration.MappedRange[0] + rng, err := declMappedRange.Range() + if err != nil { + return nil, err + } + + callHierarchyItem := protocol.CallHierarchyItem{ + Name: identifier.Name, + Kind: protocol.Function, + Tags: []protocol.SymbolTag{}, + Detail: fmt.Sprintf("%s • %s", identifier.Declaration.obj.Pkg().Path(), filepath.Base(declMappedRange.URI().Filename())), + URI: protocol.DocumentURI(declMappedRange.URI()), + Range: rng, + SelectionRange: rng, + } + return []protocol.CallHierarchyItem{callHierarchyItem}, nil +} + +// IncomingCalls returns an array of CallHierarchyIncomingCall for a file and the position within the file. +func IncomingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyIncomingCall, error) { + ctx, done := event.Start(ctx, "source.IncomingCalls") + defer done() + + refs, err := References(ctx, snapshot, fh, pos, false) + if err != nil { + if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) { + return nil, nil + } + return nil, err + } + + return toProtocolIncomingCalls(ctx, snapshot, refs) +} + +// toProtocolIncomingCalls returns an array of protocol.CallHierarchyIncomingCall for ReferenceInfo's. +// References inside same enclosure are assigned to the same enclosing function. +func toProtocolIncomingCalls(ctx context.Context, snapshot Snapshot, refs []*ReferenceInfo) ([]protocol.CallHierarchyIncomingCall, error) { + // an enclosing node could have multiple calls to a reference, we only show the enclosure + // once in the result but highlight all calls using FromRanges (ranges at which the calls occur) + var incomingCalls = map[protocol.Location]*protocol.CallHierarchyIncomingCall{} + for _, ref := range refs { + refRange, err := ref.Range() + if err != nil { + return nil, err + } + + callItem, err := enclosingNodeCallItem(snapshot, ref.pkg, ref.URI(), ref.ident.NamePos) + if err != nil { + event.Error(ctx, "error getting enclosing node", err, tag.Method.Of(ref.Name)) + continue + } + loc := protocol.Location{ + URI: callItem.URI, + Range: callItem.Range, + } + + if incomingCall, ok := incomingCalls[loc]; ok { + incomingCall.FromRanges = append(incomingCall.FromRanges, refRange) + continue + } + incomingCalls[loc] = &protocol.CallHierarchyIncomingCall{ + From: callItem, + FromRanges: []protocol.Range{refRange}, + } + } + + incomingCallItems := make([]protocol.CallHierarchyIncomingCall, 0, len(incomingCalls)) + for _, callItem := range incomingCalls { + incomingCallItems = append(incomingCallItems, *callItem) + } + return incomingCallItems, nil +} + +// enclosingNodeCallItem creates a CallHierarchyItem representing the function call at pos +func enclosingNodeCallItem(snapshot Snapshot, pkg Package, uri span.URI, pos token.Pos) (protocol.CallHierarchyItem, error) { + pgf, err := pkg.File(uri) + if err != nil { + return protocol.CallHierarchyItem{}, err + } + + var funcDecl *ast.FuncDecl + var funcLit *ast.FuncLit // innermost function literal + var litCount int + // Find the enclosing function, if any, and the number of func literals in between. + path, _ := astutil.PathEnclosingInterval(pgf.File, pos, pos) +outer: + for _, node := range path { + switch n := node.(type) { + case *ast.FuncDecl: + funcDecl = n + break outer + case *ast.FuncLit: + litCount++ + if litCount > 1 { + continue + } + funcLit = n + } + } + + nameIdent := path[len(path)-1].(*ast.File).Name + kind := protocol.Package + if funcDecl != nil { + nameIdent = funcDecl.Name + kind = protocol.Function + } + + nameStart, nameEnd := nameIdent.Pos(), nameIdent.End() + if funcLit != nil { + nameStart, nameEnd = funcLit.Type.Func, funcLit.Type.Params.Pos() + kind = protocol.Function + } + rng, err := NewMappedRange(pgf.Tok, pgf.Mapper, nameStart, nameEnd).Range() + if err != nil { + return protocol.CallHierarchyItem{}, err + } + + name := nameIdent.Name + for i := 0; i < litCount; i++ { + name += ".func()" + } + + return protocol.CallHierarchyItem{ + Name: name, + Kind: kind, + Tags: []protocol.SymbolTag{}, + Detail: fmt.Sprintf("%s • %s", pkg.PkgPath(), filepath.Base(uri.Filename())), + URI: protocol.DocumentURI(uri), + Range: rng, + SelectionRange: rng, + }, nil +} + +// OutgoingCalls returns an array of CallHierarchyOutgoingCall for a file and the position within the file. +func OutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyOutgoingCall, error) { + ctx, done := event.Start(ctx, "source.OutgoingCalls") + defer done() + + identifier, err := Identifier(ctx, snapshot, fh, pos) + if err != nil { + if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) { + return nil, nil + } + return nil, err + } + + if _, ok := identifier.Declaration.obj.Type().Underlying().(*types.Signature); !ok { + return nil, nil + } + node := identifier.Declaration.node + if node == nil { + return nil, nil + } + if len(identifier.Declaration.MappedRange) == 0 { + return nil, nil + } + declMappedRange := identifier.Declaration.MappedRange[0] + // TODO(adonovan): avoid Fileset.File call by somehow getting at + // declMappedRange.spanRange.TokFile, or making Identifier retain the + // token.File of the identifier and its declaration, since it looks up both anyway. + tokFile := identifier.pkg.FileSet().File(node.Pos()) + if tokFile == nil { + return nil, fmt.Errorf("no file for position") + } + callExprs, err := collectCallExpressions(tokFile, declMappedRange.m, node) + if err != nil { + return nil, err + } + + return toProtocolOutgoingCalls(ctx, snapshot, fh, callExprs) +} + +// collectCallExpressions collects call expression ranges inside a function. +func collectCallExpressions(tokFile *token.File, mapper *protocol.ColumnMapper, node ast.Node) ([]protocol.Range, error) { + type callPos struct { + start, end token.Pos + } + callPositions := []callPos{} + + ast.Inspect(node, func(n ast.Node) bool { + if call, ok := n.(*ast.CallExpr); ok { + var start, end token.Pos + switch n := call.Fun.(type) { + case *ast.SelectorExpr: + start, end = n.Sel.NamePos, call.Lparen + case *ast.Ident: + start, end = n.NamePos, call.Lparen + case *ast.FuncLit: + // while we don't add the function literal as an 'outgoing' call + // we still want to traverse into it + return true + default: + // ignore any other kind of call expressions + // for ex: direct function literal calls since that's not an 'outgoing' call + return false + } + callPositions = append(callPositions, callPos{start: start, end: end}) + } + return true + }) + + callRanges := []protocol.Range{} + for _, call := range callPositions { + callRange, err := NewMappedRange(tokFile, mapper, call.start, call.end).Range() + if err != nil { + return nil, err + } + callRanges = append(callRanges, callRange) + } + return callRanges, nil +} + +// toProtocolOutgoingCalls returns an array of protocol.CallHierarchyOutgoingCall for ast call expressions. +// Calls to the same function are assigned to the same declaration. +func toProtocolOutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, callRanges []protocol.Range) ([]protocol.CallHierarchyOutgoingCall, error) { + // Multiple calls could be made to the same function, defined by "same declaration + // AST node & same identifier name" to provide a unique identifier key even when + // the func is declared in a struct or interface. + type key struct { + decl ast.Node + name string + } + outgoingCalls := map[key]*protocol.CallHierarchyOutgoingCall{} + for _, callRange := range callRanges { + identifier, err := Identifier(ctx, snapshot, fh, callRange.Start) + if err != nil { + if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) { + continue + } + return nil, err + } + + // ignore calls to builtin functions + if identifier.Declaration.obj.Pkg() == nil { + continue + } + + if outgoingCall, ok := outgoingCalls[key{identifier.Declaration.node, identifier.Name}]; ok { + outgoingCall.FromRanges = append(outgoingCall.FromRanges, callRange) + continue + } + + if len(identifier.Declaration.MappedRange) == 0 { + continue + } + declMappedRange := identifier.Declaration.MappedRange[0] + rng, err := declMappedRange.Range() + if err != nil { + return nil, err + } + + outgoingCalls[key{identifier.Declaration.node, identifier.Name}] = &protocol.CallHierarchyOutgoingCall{ + To: protocol.CallHierarchyItem{ + Name: identifier.Name, + Kind: protocol.Function, + Tags: []protocol.SymbolTag{}, + Detail: fmt.Sprintf("%s • %s", identifier.Declaration.obj.Pkg().Path(), filepath.Base(declMappedRange.URI().Filename())), + URI: protocol.DocumentURI(declMappedRange.URI()), + Range: rng, + SelectionRange: rng, + }, + FromRanges: []protocol.Range{callRange}, + } + } + + outgoingCallItems := make([]protocol.CallHierarchyOutgoingCall, 0, len(outgoingCalls)) + for _, callItem := range outgoingCalls { + outgoingCallItems = append(outgoingCallItems, *callItem) + } + return outgoingCallItems, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/code_lens.go b/gopls/golang_org_x_tools_gopls/lsp/source/code_lens.go new file mode 100644 index 0000000..dd3d758 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/code_lens.go @@ -0,0 +1,248 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "go/ast" + "go/token" + "go/types" + "path/filepath" + "regexp" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/command" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" +) + +type LensFunc func(context.Context, Snapshot, FileHandle) ([]protocol.CodeLens, error) + +// LensFuncs returns the supported lensFuncs for Go files. +func LensFuncs() map[command.Command]LensFunc { + return map[command.Command]LensFunc{ + command.Generate: goGenerateCodeLens, + command.Test: runTestCodeLens, + command.RegenerateCgo: regenerateCgoLens, + command.GCDetails: toggleDetailsCodeLens, + } +} + +var ( + testRe = regexp.MustCompile("^Test[^a-z]") + benchmarkRe = regexp.MustCompile("^Benchmark[^a-z]") +) + +func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) { + codeLens := make([]protocol.CodeLens, 0) + + fns, err := TestsAndBenchmarks(ctx, snapshot, fh) + if err != nil { + return nil, err + } + puri := protocol.URIFromSpanURI(fh.URI()) + for _, fn := range fns.Tests { + cmd, err := command.NewTestCommand("run test", puri, []string{fn.Name}, nil) + if err != nil { + return nil, err + } + rng := protocol.Range{Start: fn.Rng.Start, End: fn.Rng.Start} + codeLens = append(codeLens, protocol.CodeLens{Range: rng, Command: cmd}) + } + + for _, fn := range fns.Benchmarks { + cmd, err := command.NewTestCommand("run benchmark", puri, nil, []string{fn.Name}) + if err != nil { + return nil, err + } + rng := protocol.Range{Start: fn.Rng.Start, End: fn.Rng.Start} + codeLens = append(codeLens, protocol.CodeLens{Range: rng, Command: cmd}) + } + + if len(fns.Benchmarks) > 0 { + _, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage) + if err != nil { + return nil, err + } + // add a code lens to the top of the file which runs all benchmarks in the file + rng, err := NewMappedRange(pgf.Tok, pgf.Mapper, pgf.File.Package, pgf.File.Package).Range() + if err != nil { + return nil, err + } + var benches []string + for _, fn := range fns.Benchmarks { + benches = append(benches, fn.Name) + } + cmd, err := command.NewTestCommand("run file benchmarks", puri, nil, benches) + if err != nil { + return nil, err + } + codeLens = append(codeLens, protocol.CodeLens{Range: rng, Command: cmd}) + } + return codeLens, nil +} + +type testFn struct { + Name string + Rng protocol.Range +} + +type testFns struct { + Tests []testFn + Benchmarks []testFn +} + +func TestsAndBenchmarks(ctx context.Context, snapshot Snapshot, fh FileHandle) (testFns, error) { + var out testFns + + if !strings.HasSuffix(fh.URI().Filename(), "_test.go") { + return out, nil + } + pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage) + if err != nil { + return out, err + } + + for _, d := range pgf.File.Decls { + fn, ok := d.(*ast.FuncDecl) + if !ok { + continue + } + + rng, err := NewMappedRange(pgf.Tok, pgf.Mapper, fn.Pos(), fn.End()).Range() + if err != nil { + return out, err + } + + if matchTestFunc(fn, pkg, testRe, "T") { + out.Tests = append(out.Tests, testFn{fn.Name.Name, rng}) + } + + if matchTestFunc(fn, pkg, benchmarkRe, "B") { + out.Benchmarks = append(out.Benchmarks, testFn{fn.Name.Name, rng}) + } + } + + return out, nil +} + +func matchTestFunc(fn *ast.FuncDecl, pkg Package, nameRe *regexp.Regexp, paramID string) bool { + // Make sure that the function name matches a test function. + if !nameRe.MatchString(fn.Name.Name) { + return false + } + info := pkg.GetTypesInfo() + if info == nil { + return false + } + obj := info.ObjectOf(fn.Name) + if obj == nil { + return false + } + sig, ok := obj.Type().(*types.Signature) + if !ok { + return false + } + // Test functions should have only one parameter. + if sig.Params().Len() != 1 { + return false + } + + // Check the type of the only parameter + paramTyp, ok := sig.Params().At(0).Type().(*types.Pointer) + if !ok { + return false + } + named, ok := paramTyp.Elem().(*types.Named) + if !ok { + return false + } + namedObj := named.Obj() + if namedObj.Pkg().Path() != "testing" { + return false + } + return namedObj.Id() == paramID +} + +func goGenerateCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) { + pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) + if err != nil { + return nil, err + } + const ggDirective = "//go:generate" + for _, c := range pgf.File.Comments { + for _, l := range c.List { + if !strings.HasPrefix(l.Text, ggDirective) { + continue + } + rng, err := NewMappedRange(pgf.Tok, pgf.Mapper, l.Pos(), l.Pos()+token.Pos(len(ggDirective))).Range() + if err != nil { + return nil, err + } + dir := protocol.URIFromSpanURI(span.URIFromPath(filepath.Dir(fh.URI().Filename()))) + nonRecursiveCmd, err := command.NewGenerateCommand("run go generate", command.GenerateArgs{Dir: dir, Recursive: false}) + if err != nil { + return nil, err + } + recursiveCmd, err := command.NewGenerateCommand("run go generate ./...", command.GenerateArgs{Dir: dir, Recursive: true}) + if err != nil { + return nil, err + } + return []protocol.CodeLens{ + {Range: rng, Command: recursiveCmd}, + {Range: rng, Command: nonRecursiveCmd}, + }, nil + + } + } + return nil, nil +} + +func regenerateCgoLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) { + pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) + if err != nil { + return nil, err + } + var c *ast.ImportSpec + for _, imp := range pgf.File.Imports { + if imp.Path.Value == `"C"` { + c = imp + } + } + if c == nil { + return nil, nil + } + rng, err := NewMappedRange(pgf.Tok, pgf.Mapper, c.Pos(), c.End()).Range() + if err != nil { + return nil, err + } + puri := protocol.URIFromSpanURI(fh.URI()) + cmd, err := command.NewRegenerateCgoCommand("regenerate cgo definitions", command.URIArg{URI: puri}) + if err != nil { + return nil, err + } + return []protocol.CodeLens{{Range: rng, Command: cmd}}, nil +} + +func toggleDetailsCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) { + _, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage) + if err != nil { + return nil, err + } + if !pgf.File.Package.IsValid() { + // Without a package name we have nowhere to put the codelens, so give up. + return nil, nil + } + rng, err := NewMappedRange(pgf.Tok, pgf.Mapper, pgf.File.Package, pgf.File.Package).Range() + if err != nil { + return nil, err + } + puri := protocol.URIFromSpanURI(fh.URI()) + cmd, err := command.NewGCDetailsCommand("Toggle gc annotation details", puri) + if err != nil { + return nil, err + } + return []protocol.CodeLens{{Range: rng, Command: cmd}}, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/comment.go b/gopls/golang_org_x_tools_gopls/lsp/source/comment.go new file mode 100644 index 0000000..ff6d11f --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/comment.go @@ -0,0 +1,384 @@ +// 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. + +//go:build !go1.19 +// +build !go1.19 + +package source + +import ( + "bytes" + "io" + "regexp" + "strings" + "unicode" + "unicode/utf8" +) + +// CommentToMarkdown converts comment text to formatted markdown. +// The comment was prepared by DocReader, +// so it is known not to have leading, trailing blank lines +// nor to have trailing spaces at the end of lines. +// The comment markers have already been removed. +// +// Each line is converted into a markdown line and empty lines are just converted to +// newlines. Heading are prefixed with `### ` to make it a markdown heading. +// +// A span of indented lines retains a 4 space prefix block, with the common indent +// prefix removed unless empty, in which case it will be converted to a newline. +// +// URLs in the comment text are converted into links. +func CommentToMarkdown(text string) string { + buf := &bytes.Buffer{} + commentToMarkdown(buf, text) + return buf.String() +} + +var ( + mdNewline = []byte("\n") + mdHeader = []byte("### ") + mdIndent = []byte(" ") + mdLinkStart = []byte("[") + mdLinkDiv = []byte("](") + mdLinkEnd = []byte(")") +) + +func commentToMarkdown(w io.Writer, text string) { + blocks := blocks(text) + for i, b := range blocks { + switch b.op { + case opPara: + for _, line := range b.lines { + emphasize(w, line, true) + } + case opHead: + // The header block can consist of only one line. + // However, check the number of lines, just in case. + if len(b.lines) == 0 { + // Skip this block. + continue + } + header := b.lines[0] + + w.Write(mdHeader) + commentEscape(w, header, true) + // Header doesn't end with \n unlike the lines of other blocks. + w.Write(mdNewline) + case opPre: + for _, line := range b.lines { + if isBlank(line) { + w.Write(mdNewline) + continue + } + w.Write(mdIndent) + w.Write([]byte(line)) + } + } + + if i < len(blocks)-1 { + w.Write(mdNewline) + } + } +} + +const ( + ulquo = "“" + urquo = "”" +) + +var ( + markdownEscape = regexp.MustCompile(`([\\\x60*{}[\]()#+\-.!_>~|"$%&'\/:;<=?@^])`) + + unicodeQuoteReplacer = strings.NewReplacer("``", ulquo, "''", urquo) +) + +// commentEscape escapes comment text for markdown. If nice is set, +// also turn double ` and ' into “ and ”. +func commentEscape(w io.Writer, text string, nice bool) { + if nice { + text = convertQuotes(text) + } + text = escapeRegex(text) + w.Write([]byte(text)) +} + +func convertQuotes(text string) string { + return unicodeQuoteReplacer.Replace(text) +} + +func escapeRegex(text string) string { + return markdownEscape.ReplaceAllString(text, `\$1`) +} + +func emphasize(w io.Writer, line string, nice bool) { + for { + m := matchRx.FindStringSubmatchIndex(line) + if m == nil { + break + } + // m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is urlRx) + + // write text before match + commentEscape(w, line[0:m[0]], nice) + + // adjust match for URLs + match := line[m[0]:m[1]] + if strings.Contains(match, "://") { + m0, m1 := m[0], m[1] + for _, s := range []string{"()", "{}", "[]"} { + open, close := s[:1], s[1:] // E.g., "(" and ")" + // require opening parentheses before closing parentheses (#22285) + if i := strings.Index(match, close); i >= 0 && i < strings.Index(match, open) { + m1 = m0 + i + match = line[m0:m1] + } + // require balanced pairs of parentheses (#5043) + for i := 0; strings.Count(match, open) != strings.Count(match, close) && i < 10; i++ { + m1 = strings.LastIndexAny(line[:m1], s) + match = line[m0:m1] + } + } + if m1 != m[1] { + // redo matching with shortened line for correct indices + m = matchRx.FindStringSubmatchIndex(line[:m[0]+len(match)]) + } + } + + // Following code has been modified from go/doc since words is always + // nil. All html formatting has also been transformed into markdown formatting + + // analyze match + url := "" + if m[2] >= 0 { + url = match + } + + // write match + if len(url) > 0 { + w.Write(mdLinkStart) + } + + commentEscape(w, match, nice) + + if len(url) > 0 { + w.Write(mdLinkDiv) + w.Write([]byte(urlReplacer.Replace(url))) + w.Write(mdLinkEnd) + } + + // advance + line = line[m[1]:] + } + commentEscape(w, line, nice) +} + +// Everything from here on is a copy of go/doc/comment.go + +const ( + // Regexp for Go identifiers + identRx = `[\pL_][\pL_0-9]*` + + // Regexp for URLs + // Match parens, and check later for balance - see #5043, #22285 + // Match .,:;?! within path, but not at end - see #18139, #16565 + // This excludes some rare yet valid urls ending in common punctuation + // in order to allow sentences ending in URLs. + + // protocol (required) e.g. http + protoPart = `(https?|ftp|file|gopher|mailto|nntp)` + // host (required) e.g. www.example.com or [::1]:8080 + hostPart = `([a-zA-Z0-9_@\-.\[\]:]+)` + // path+query+fragment (optional) e.g. /path/index.html?q=foo#bar + pathPart = `([.,:;?!]*[a-zA-Z0-9$'()*+&#=@~_/\-\[\]%])*` + + urlRx = protoPart + `://` + hostPart + pathPart +) + +var ( + matchRx = regexp.MustCompile(`(` + urlRx + `)|(` + identRx + `)`) + urlReplacer = strings.NewReplacer(`(`, `\(`, `)`, `\)`) +) + +func indentLen(s string) int { + i := 0 + for i < len(s) && (s[i] == ' ' || s[i] == '\t') { + i++ + } + return i +} + +func isBlank(s string) bool { + return len(s) == 0 || (len(s) == 1 && s[0] == '\n') +} + +func commonPrefix(a, b string) string { + i := 0 + for i < len(a) && i < len(b) && a[i] == b[i] { + i++ + } + return a[0:i] +} + +func unindent(block []string) { + if len(block) == 0 { + return + } + + // compute maximum common white prefix + prefix := block[0][0:indentLen(block[0])] + for _, line := range block { + if !isBlank(line) { + prefix = commonPrefix(prefix, line) + } + } + n := len(prefix) + + // remove + for i, line := range block { + if !isBlank(line) { + block[i] = line[n:] + } + } +} + +// heading returns the trimmed line if it passes as a section heading; +// otherwise it returns the empty string. +func heading(line string) string { + line = strings.TrimSpace(line) + if len(line) == 0 { + return "" + } + + // a heading must start with an uppercase letter + r, _ := utf8.DecodeRuneInString(line) + if !unicode.IsLetter(r) || !unicode.IsUpper(r) { + return "" + } + + // it must end in a letter or digit: + r, _ = utf8.DecodeLastRuneInString(line) + if !unicode.IsLetter(r) && !unicode.IsDigit(r) { + return "" + } + + // exclude lines with illegal characters. we allow "()," + if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") { + return "" + } + + // allow "'" for possessive "'s" only + for b := line; ; { + i := strings.IndexRune(b, '\'') + if i < 0 { + break + } + if i+1 >= len(b) || b[i+1] != 's' || (i+2 < len(b) && b[i+2] != ' ') { + return "" // not followed by "s " + } + b = b[i+2:] + } + + // allow "." when followed by non-space + for b := line; ; { + i := strings.IndexRune(b, '.') + if i < 0 { + break + } + if i+1 >= len(b) || b[i+1] == ' ' { + return "" // not followed by non-space + } + b = b[i+1:] + } + + return line +} + +type op int + +const ( + opPara op = iota + opHead + opPre +) + +type block struct { + op op + lines []string +} + +func blocks(text string) []block { + var ( + out []block + para []string + + lastWasBlank = false + lastWasHeading = false + ) + + close := func() { + if para != nil { + out = append(out, block{opPara, para}) + para = nil + } + } + + lines := strings.SplitAfter(text, "\n") + unindent(lines) + for i := 0; i < len(lines); { + line := lines[i] + if isBlank(line) { + // close paragraph + close() + i++ + lastWasBlank = true + continue + } + if indentLen(line) > 0 { + // close paragraph + close() + + // count indented or blank lines + j := i + 1 + for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) { + j++ + } + // but not trailing blank lines + for j > i && isBlank(lines[j-1]) { + j-- + } + pre := lines[i:j] + i = j + + unindent(pre) + + // put those lines in a pre block + out = append(out, block{opPre, pre}) + lastWasHeading = false + continue + } + + if lastWasBlank && !lastWasHeading && i+2 < len(lines) && + isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 { + // current line is non-blank, surrounded by blank lines + // and the next non-blank line is not indented: this + // might be a heading. + if head := heading(line); head != "" { + close() + out = append(out, block{opHead, []string{head}}) + i += 2 + lastWasHeading = true + continue + } + } + + // open paragraph + lastWasBlank = false + lastWasHeading = false + para = append(para, lines[i]) + i++ + } + close() + + return out +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/comment_go118.go b/gopls/golang_org_x_tools_gopls/lsp/source/comment_go118.go new file mode 100644 index 0000000..0503670 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/comment_go118.go @@ -0,0 +1,37 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.19 +// +build go1.19 + +package source + +// Starting with go1.19, the formatting of comments has changed, and there +// is a new package (go/doc/comment) for processing them. +// As long as gopls has to compile under earlier versions, tests +// have to pass with both the old and new code, which produce +// slightly different results. (cmd/test/definition.go, source/comment_test.go, +// and source/source_test.go) Each of the test files checks the results +// with a function, tests.CheckSameMarkdown, that accepts both the old and the new +// results. (The old code escapes many characters the new code does not, +// and the new code sometimes adds a blank line.) + +// When gopls no longer needs to compile with go1.18, the old comment.go should +// be replaced by this file, the golden test files should be updated. +// (and checkSameMarkdown() could be replaced by a simple comparison.) + +import "go/doc/comment" + +// CommentToMarkdown converts comment text to formatted markdown. +// The comment was prepared by DocReader, +// so it is known not to have leading, trailing blank lines +// nor to have trailing spaces at the end of lines. +// The comment markers have already been removed. +func CommentToMarkdown(text string) string { + var p comment.Parser + doc := p.Parse(text) + var pr comment.Printer + easy := pr.Markdown(doc) + return string(easy) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/diagnostics.go b/gopls/golang_org_x_tools_gopls/lsp/source/diagnostics.go new file mode 100644 index 0000000..4538eaa --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/diagnostics.go @@ -0,0 +1,84 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" +) + +type SuggestedFix struct { + Title string + Edits map[span.URI][]protocol.TextEdit + Command *protocol.Command + ActionKind protocol.CodeActionKind +} + +type RelatedInformation struct { + URI span.URI + Range protocol.Range + Message string +} + +func Analyze(ctx context.Context, snapshot Snapshot, pkg Package, includeConvenience bool) (map[span.URI][]*Diagnostic, error) { + // Exit early if the context has been canceled. This also protects us + // from a race on Options, see golang/go#36699. + if ctx.Err() != nil { + return nil, ctx.Err() + } + + categories := []map[string]*Analyzer{} + if includeConvenience { + categories = append(categories, snapshot.View().Options().ConvenienceAnalyzers) + } + // If we had type errors, don't run any other analyzers. + if !pkg.HasTypeErrors() { + categories = append(categories, snapshot.View().Options().DefaultAnalyzers, snapshot.View().Options().StaticcheckAnalyzers) + } + var analyzers []*Analyzer + for _, cat := range categories { + for _, a := range cat { + analyzers = append(analyzers, a) + } + } + + analysisDiagnostics, err := snapshot.Analyze(ctx, pkg.ID(), analyzers) + if err != nil { + return nil, err + } + + reports := map[span.URI][]*Diagnostic{} + // Report diagnostics and errors from root analyzers. + for _, diag := range analysisDiagnostics { + reports[diag.URI] = append(reports[diag.URI], diag) + } + return reports, nil +} + +func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (VersionedFileIdentity, []*Diagnostic, error) { + fh, err := snapshot.GetVersionedFile(ctx, uri) + if err != nil { + return VersionedFileIdentity{}, nil, err + } + pkg, _, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return VersionedFileIdentity{}, nil, err + } + diagnostics, err := snapshot.DiagnosePackage(ctx, pkg) + if err != nil { + return VersionedFileIdentity{}, nil, err + } + fileDiags := diagnostics[fh.URI()] + if !pkg.HasListOrParseErrors() { + analysisDiags, err := Analyze(ctx, snapshot, pkg, false) + if err != nil { + return VersionedFileIdentity{}, nil, err + } + fileDiags = append(fileDiags, analysisDiags[fh.URI()]...) + } + return fh.VersionedFileIdentity(), fileDiags, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/extract.go b/gopls/golang_org_x_tools_gopls/lsp/source/extract.go new file mode 100644 index 0000000..ab92b32 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/extract.go @@ -0,0 +1,1343 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "go/types" + "sort" + "strings" + "text/scanner" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/analysisinternal" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/bug" +) + +func extractVariable(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, _ *types.Package, info *types.Info) (*analysis.SuggestedFix, error) { + tokFile := fset.File(file.Pos()) + expr, path, ok, err := CanExtractVariable(rng, file) + if !ok { + return nil, fmt.Errorf("extractVariable: cannot extract %s: %v", fset.Position(rng.Start), err) + } + + // Create new AST node for extracted code. + var lhsNames []string + switch expr := expr.(type) { + // TODO: stricter rules for selectorExpr. + case *ast.BasicLit, *ast.CompositeLit, *ast.IndexExpr, *ast.SliceExpr, + *ast.UnaryExpr, *ast.BinaryExpr, *ast.SelectorExpr: + lhsName, _ := generateAvailableIdentifier(expr.Pos(), file, path, info, "x", 0) + lhsNames = append(lhsNames, lhsName) + case *ast.CallExpr: + tup, ok := info.TypeOf(expr).(*types.Tuple) + if !ok { + // If the call expression only has one return value, we can treat it the + // same as our standard extract variable case. + lhsName, _ := generateAvailableIdentifier(expr.Pos(), file, path, info, "x", 0) + lhsNames = append(lhsNames, lhsName) + break + } + idx := 0 + for i := 0; i < tup.Len(); i++ { + // Generate a unique variable for each return value. + var lhsName string + lhsName, idx = generateAvailableIdentifier(expr.Pos(), file, path, info, "x", idx) + lhsNames = append(lhsNames, lhsName) + } + default: + return nil, fmt.Errorf("cannot extract %T", expr) + } + + insertBeforeStmt := analysisinternal.StmtToInsertVarBefore(path) + if insertBeforeStmt == nil { + return nil, fmt.Errorf("cannot find location to insert extraction") + } + indent, err := calculateIndentation(src, tokFile, insertBeforeStmt) + if err != nil { + return nil, err + } + newLineIndent := "\n" + indent + + lhs := strings.Join(lhsNames, ", ") + assignStmt := &ast.AssignStmt{ + Lhs: []ast.Expr{ast.NewIdent(lhs)}, + Tok: token.DEFINE, + Rhs: []ast.Expr{expr}, + } + var buf bytes.Buffer + if err := format.Node(&buf, fset, assignStmt); err != nil { + return nil, err + } + assignment := strings.ReplaceAll(buf.String(), "\n", newLineIndent) + newLineIndent + + return &analysis.SuggestedFix{ + TextEdits: []analysis.TextEdit{ + { + Pos: insertBeforeStmt.Pos(), + End: insertBeforeStmt.Pos(), + NewText: []byte(assignment), + }, + { + Pos: rng.Start, + End: rng.End, + NewText: []byte(lhs), + }, + }, + }, nil +} + +// CanExtractVariable reports whether the code in the given range can be +// extracted to a variable. +func CanExtractVariable(rng span.Range, file *ast.File) (ast.Expr, []ast.Node, bool, error) { + if rng.Start == rng.End { + return nil, nil, false, fmt.Errorf("start and end are equal") + } + path, _ := astutil.PathEnclosingInterval(file, rng.Start, rng.End) + if len(path) == 0 { + return nil, nil, false, fmt.Errorf("no path enclosing interval") + } + for _, n := range path { + if _, ok := n.(*ast.ImportSpec); ok { + return nil, nil, false, fmt.Errorf("cannot extract variable in an import block") + } + } + node := path[0] + if rng.Start != node.Pos() || rng.End != node.End() { + return nil, nil, false, fmt.Errorf("range does not map to an AST node") + } + expr, ok := node.(ast.Expr) + if !ok { + return nil, nil, false, fmt.Errorf("node is not an expression") + } + switch expr.(type) { + case *ast.BasicLit, *ast.CompositeLit, *ast.IndexExpr, *ast.CallExpr, + *ast.SliceExpr, *ast.UnaryExpr, *ast.BinaryExpr, *ast.SelectorExpr: + return expr, path, true, nil + } + return nil, nil, false, fmt.Errorf("cannot extract an %T to a variable", expr) +} + +// Calculate indentation for insertion. +// When inserting lines of code, we must ensure that the lines have consistent +// formatting (i.e. the proper indentation). To do so, we observe the indentation on the +// line of code on which the insertion occurs. +func calculateIndentation(content []byte, tok *token.File, insertBeforeStmt ast.Node) (string, error) { + line := tok.Line(insertBeforeStmt.Pos()) + lineOffset, err := safetoken.Offset(tok, tok.LineStart(line)) + if err != nil { + return "", err + } + stmtOffset, err := safetoken.Offset(tok, insertBeforeStmt.Pos()) + if err != nil { + return "", err + } + return string(content[lineOffset:stmtOffset]), nil +} + +// generateAvailableIdentifier adjusts the new function name until there are no collisions in scope. +// Possible collisions include other function and variable names. Returns the next index to check for prefix. +func generateAvailableIdentifier(pos token.Pos, file *ast.File, path []ast.Node, info *types.Info, prefix string, idx int) (string, int) { + scopes := CollectScopes(info, path, pos) + return generateIdentifier(idx, prefix, func(name string) bool { + return file.Scope.Lookup(name) != nil || !isValidName(name, scopes) + }) +} + +func generateIdentifier(idx int, prefix string, hasCollision func(string) bool) (string, int) { + name := prefix + if idx != 0 { + name += fmt.Sprintf("%d", idx) + } + for hasCollision(name) { + idx++ + name = fmt.Sprintf("%v%d", prefix, idx) + } + return name, idx + 1 +} + +// isValidName checks for variable collision in scope. +func isValidName(name string, scopes []*types.Scope) bool { + for _, scope := range scopes { + if scope == nil { + continue + } + if scope.Lookup(name) != nil { + return false + } + } + return true +} + +// returnVariable keeps track of the information we need to properly introduce a new variable +// that we will return in the extracted function. +type returnVariable struct { + // name is the identifier that is used on the left-hand side of the call to + // the extracted function. + name ast.Expr + // decl is the declaration of the variable. It is used in the type signature of the + // extracted function and for variable declarations. + decl *ast.Field + // zeroVal is the "zero value" of the type of the variable. It is used in a return + // statement in the extracted function. + zeroVal ast.Expr +} + +// extractMethod refactors the selected block of code into a new method. +func extractMethod(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) { + return extractFunctionMethod(fset, rng, src, file, pkg, info, true) +} + +// extractFunction refactors the selected block of code into a new function. +func extractFunction(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) { + return extractFunctionMethod(fset, rng, src, file, pkg, info, false) +} + +// extractFunctionMethod refactors the selected block of code into a new function/method. +// It also replaces the selected block of code with a call to the extracted +// function. First, we manually adjust the selection range. We remove trailing +// and leading whitespace characters to ensure the range is precisely bounded +// by AST nodes. Next, we determine the variables that will be the parameters +// and return values of the extracted function/method. Lastly, we construct the call +// of the function/method and insert this call as well as the extracted function/method into +// their proper locations. +func extractFunctionMethod(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info, isMethod bool) (*analysis.SuggestedFix, error) { + errorPrefix := "extractFunction" + if isMethod { + errorPrefix = "extractMethod" + } + + tok := fset.File(file.Pos()) + if tok == nil { + return nil, bug.Errorf("no file for position") + } + p, ok, methodOk, err := CanExtractFunction(tok, rng, src, file) + if (!ok && !isMethod) || (!methodOk && isMethod) { + return nil, fmt.Errorf("%s: cannot extract %s: %v", errorPrefix, + fset.Position(rng.Start), err) + } + tok, path, rng, outer, start := p.tok, p.path, p.rng, p.outer, p.start + fileScope := info.Scopes[file] + if fileScope == nil { + return nil, fmt.Errorf("%s: file scope is empty", errorPrefix) + } + pkgScope := fileScope.Parent() + if pkgScope == nil { + return nil, fmt.Errorf("%s: package scope is empty", errorPrefix) + } + + // A return statement is non-nested if its parent node is equal to the parent node + // of the first node in the selection. These cases must be handled separately because + // non-nested return statements are guaranteed to execute. + var retStmts []*ast.ReturnStmt + var hasNonNestedReturn bool + startParent := findParent(outer, start) + ast.Inspect(outer, func(n ast.Node) bool { + if n == nil { + return false + } + if n.Pos() < rng.Start || n.End() > rng.End { + return n.Pos() <= rng.End + } + ret, ok := n.(*ast.ReturnStmt) + if !ok { + return true + } + if findParent(outer, n) == startParent { + hasNonNestedReturn = true + } + retStmts = append(retStmts, ret) + return false + }) + containsReturnStatement := len(retStmts) > 0 + + // Now that we have determined the correct range for the selection block, + // we must determine the signature of the extracted function. We will then replace + // the block with an assignment statement that calls the extracted function with + // the appropriate parameters and return values. + variables, err := collectFreeVars(info, file, fileScope, pkgScope, rng, path[0]) + if err != nil { + return nil, err + } + + var ( + receiverUsed bool + receiver *ast.Field + receiverName string + receiverObj types.Object + ) + if isMethod { + if outer == nil || outer.Recv == nil || len(outer.Recv.List) == 0 { + return nil, fmt.Errorf("%s: cannot extract need method receiver", errorPrefix) + } + receiver = outer.Recv.List[0] + if len(receiver.Names) == 0 || receiver.Names[0] == nil { + return nil, fmt.Errorf("%s: cannot extract need method receiver name", errorPrefix) + } + recvName := receiver.Names[0] + receiverName = recvName.Name + receiverObj = info.ObjectOf(recvName) + } + + var ( + params, returns []ast.Expr // used when calling the extracted function + paramTypes, returnTypes []*ast.Field // used in the signature of the extracted function + uninitialized []types.Object // vars we will need to initialize before the call + ) + + // Avoid duplicates while traversing vars and uninitialized. + seenVars := make(map[types.Object]ast.Expr) + seenUninitialized := make(map[types.Object]struct{}) + + // Some variables on the left-hand side of our assignment statement may be free. If our + // selection begins in the same scope in which the free variable is defined, we can + // redefine it in our assignment statement. See the following example, where 'b' and + // 'err' (both free variables) can be redefined in the second funcCall() while maintaining + // correctness. + // + // + // Not Redefined: + // + // a, err := funcCall() + // var b int + // b, err = funcCall() + // + // Redefined: + // + // a, err := funcCall() + // b, err := funcCall() + // + // We track the number of free variables that can be redefined to maintain our preference + // of using "x, y, z := fn()" style assignment statements. + var canRedefineCount int + + // Each identifier in the selected block must become (1) a parameter to the + // extracted function, (2) a return value of the extracted function, or (3) a local + // variable in the extracted function. Determine the outcome(s) for each variable + // based on whether it is free, altered within the selected block, and used outside + // of the selected block. + for _, v := range variables { + if _, ok := seenVars[v.obj]; ok { + continue + } + if v.obj.Name() == "_" { + // The blank identifier is always a local variable + continue + } + typ := analysisinternal.TypeExpr(file, pkg, v.obj.Type()) + if typ == nil { + return nil, fmt.Errorf("nil AST expression for type: %v", v.obj.Name()) + } + seenVars[v.obj] = typ + identifier := ast.NewIdent(v.obj.Name()) + // An identifier must meet three conditions to become a return value of the + // extracted function. (1) its value must be defined or reassigned within + // the selection (isAssigned), (2) it must be used at least once after the + // selection (isUsed), and (3) its first use after the selection + // cannot be its own reassignment or redefinition (objOverriden). + if v.obj.Parent() == nil { + return nil, fmt.Errorf("parent nil") + } + isUsed, firstUseAfter := objUsed(info, span.NewRange(tok, rng.End, v.obj.Parent().End()), v.obj) + if v.assigned && isUsed && !varOverridden(info, firstUseAfter, v.obj, v.free, outer) { + returnTypes = append(returnTypes, &ast.Field{Type: typ}) + returns = append(returns, identifier) + if !v.free { + uninitialized = append(uninitialized, v.obj) + } else if v.obj.Parent().Pos() == startParent.Pos() { + canRedefineCount++ + } + } + // An identifier must meet two conditions to become a parameter of the + // extracted function. (1) it must be free (isFree), and (2) its first + // use within the selection cannot be its own definition (isDefined). + if v.free && !v.defined { + // Skip the selector for a method. + if isMethod && v.obj == receiverObj { + receiverUsed = true + continue + } + params = append(params, identifier) + paramTypes = append(paramTypes, &ast.Field{ + Names: []*ast.Ident{identifier}, + Type: typ, + }) + } + } + + // Find the function literal that encloses the selection. The enclosing function literal + // may not be the enclosing function declaration (i.e. 'outer'). For example, in the + // following block: + // + // func main() { + // ast.Inspect(node, func(n ast.Node) bool { + // v := 1 // this line extracted + // return true + // }) + // } + // + // 'outer' is main(). However, the extracted selection most directly belongs to + // the anonymous function literal, the second argument of ast.Inspect(). We use the + // enclosing function literal to determine the proper return types for return statements + // within the selection. We still need the enclosing function declaration because this is + // the top-level declaration. We inspect the top-level declaration to look for variables + // as well as for code replacement. + enclosing := outer.Type + for _, p := range path { + if p == enclosing { + break + } + if fl, ok := p.(*ast.FuncLit); ok { + enclosing = fl.Type + break + } + } + + // We put the selection in a constructed file. We can then traverse and edit + // the extracted selection without modifying the original AST. + startOffset, err := safetoken.Offset(tok, rng.Start) + if err != nil { + return nil, err + } + endOffset, err := safetoken.Offset(tok, rng.End) + if err != nil { + return nil, err + } + selection := src[startOffset:endOffset] + extractedBlock, err := parseBlockStmt(fset, selection) + if err != nil { + return nil, err + } + + // We need to account for return statements in the selected block, as they will complicate + // the logical flow of the extracted function. See the following example, where ** denotes + // the range to be extracted. + // + // Before: + // + // func _() int { + // a := 1 + // b := 2 + // **if a == b { + // return a + // }** + // ... + // } + // + // After: + // + // func _() int { + // a := 1 + // b := 2 + // cond0, ret0 := x0(a, b) + // if cond0 { + // return ret0 + // } + // ... + // } + // + // func x0(a int, b int) (bool, int) { + // if a == b { + // return true, a + // } + // return false, 0 + // } + // + // We handle returns by adding an additional boolean return value to the extracted function. + // This bool reports whether the original function would have returned. Because the + // extracted selection contains a return statement, we must also add the types in the + // return signature of the enclosing function to the return signature of the + // extracted function. We then add an extra if statement checking this boolean value + // in the original function. If the condition is met, the original function should + // return a value, mimicking the functionality of the original return statement(s) + // in the selection. + // + // If there is a return that is guaranteed to execute (hasNonNestedReturns=true), then + // we don't need to include this additional condition check and can simply return. + // + // Before: + // + // func _() int { + // a := 1 + // b := 2 + // **if a == b { + // return a + // } + // return b** + // } + // + // After: + // + // func _() int { + // a := 1 + // b := 2 + // return x0(a, b) + // } + // + // func x0(a int, b int) int { + // if a == b { + // return a + // } + // return b + // } + + var retVars []*returnVariable + var ifReturn *ast.IfStmt + if containsReturnStatement { + if !hasNonNestedReturn { + // The selected block contained return statements, so we have to modify the + // signature of the extracted function as described above. Adjust all of + // the return statements in the extracted function to reflect this change in + // signature. + if err := adjustReturnStatements(returnTypes, seenVars, fset, file, + pkg, extractedBlock); err != nil { + return nil, err + } + } + // Collect the additional return values and types needed to accommodate return + // statements in the selection. Update the type signature of the extracted + // function and construct the if statement that will be inserted in the enclosing + // function. + retVars, ifReturn, err = generateReturnInfo(enclosing, pkg, path, file, info, fset, rng.Start, hasNonNestedReturn) + if err != nil { + return nil, err + } + } + + // Add a return statement to the end of the new function. This return statement must include + // the values for the types of the original extracted function signature and (if a return + // statement is present in the selection) enclosing function signature. + // This only needs to be done if the selections does not have a non-nested return, otherwise + // it already terminates with a return statement. + hasReturnValues := len(returns)+len(retVars) > 0 + if hasReturnValues && !hasNonNestedReturn { + extractedBlock.List = append(extractedBlock.List, &ast.ReturnStmt{ + Results: append(returns, getZeroVals(retVars)...), + }) + } + + // Construct the appropriate call to the extracted function. + // We must meet two conditions to use ":=" instead of '='. (1) there must be at least + // one variable on the lhs that is uninitialized (non-free) prior to the assignment. + // (2) all of the initialized (free) variables on the lhs must be able to be redefined. + sym := token.ASSIGN + canDefineCount := len(uninitialized) + canRedefineCount + canDefine := len(uninitialized)+len(retVars) > 0 && canDefineCount == len(returns) + if canDefine { + sym = token.DEFINE + } + var name, funName string + if isMethod { + name = "newMethod" + // TODO(suzmue): generate a name that does not conflict for "newMethod". + funName = name + } else { + name = "newFunction" + funName, _ = generateAvailableIdentifier(rng.Start, file, path, info, name, 0) + } + extractedFunCall := generateFuncCall(hasNonNestedReturn, hasReturnValues, params, + append(returns, getNames(retVars)...), funName, sym, receiverName) + + // Build the extracted function. + newFunc := &ast.FuncDecl{ + Name: ast.NewIdent(funName), + Type: &ast.FuncType{ + Params: &ast.FieldList{List: paramTypes}, + Results: &ast.FieldList{List: append(returnTypes, getDecls(retVars)...)}, + }, + Body: extractedBlock, + } + if isMethod { + var names []*ast.Ident + if receiverUsed { + names = append(names, ast.NewIdent(receiverName)) + } + newFunc.Recv = &ast.FieldList{ + List: []*ast.Field{{ + Names: names, + Type: receiver.Type, + }}, + } + } + + // Create variable declarations for any identifiers that need to be initialized prior to + // calling the extracted function. We do not manually initialize variables if every return + // value is uninitialized. We can use := to initialize the variables in this situation. + var declarations []ast.Stmt + if canDefineCount != len(returns) { + declarations = initializeVars(uninitialized, retVars, seenUninitialized, seenVars) + } + + var declBuf, replaceBuf, newFuncBuf, ifBuf, commentBuf bytes.Buffer + if err := format.Node(&declBuf, fset, declarations); err != nil { + return nil, err + } + if err := format.Node(&replaceBuf, fset, extractedFunCall); err != nil { + return nil, err + } + if ifReturn != nil { + if err := format.Node(&ifBuf, fset, ifReturn); err != nil { + return nil, err + } + } + if err := format.Node(&newFuncBuf, fset, newFunc); err != nil { + return nil, err + } + // Find all the comments within the range and print them to be put somewhere. + // TODO(suzmue): print these in the extracted function at the correct place. + for _, cg := range file.Comments { + if cg.Pos().IsValid() && cg.Pos() < rng.End && cg.Pos() >= rng.Start { + for _, c := range cg.List { + fmt.Fprintln(&commentBuf, c.Text) + } + } + } + + // We're going to replace the whole enclosing function, + // so preserve the text before and after the selected block. + outerStart, err := safetoken.Offset(tok, outer.Pos()) + if err != nil { + return nil, err + } + outerEnd, err := safetoken.Offset(tok, outer.End()) + if err != nil { + return nil, err + } + before := src[outerStart:startOffset] + after := src[endOffset:outerEnd] + indent, err := calculateIndentation(src, tok, start) + if err != nil { + return nil, err + } + newLineIndent := "\n" + indent + + var fullReplacement strings.Builder + fullReplacement.Write(before) + if commentBuf.Len() > 0 { + comments := strings.ReplaceAll(commentBuf.String(), "\n", newLineIndent) + fullReplacement.WriteString(comments) + } + if declBuf.Len() > 0 { // add any initializations, if needed + initializations := strings.ReplaceAll(declBuf.String(), "\n", newLineIndent) + + newLineIndent + fullReplacement.WriteString(initializations) + } + fullReplacement.Write(replaceBuf.Bytes()) // call the extracted function + if ifBuf.Len() > 0 { // add the if statement below the function call, if needed + ifstatement := newLineIndent + + strings.ReplaceAll(ifBuf.String(), "\n", newLineIndent) + fullReplacement.WriteString(ifstatement) + } + fullReplacement.Write(after) + fullReplacement.WriteString("\n\n") // add newlines after the enclosing function + fullReplacement.Write(newFuncBuf.Bytes()) // insert the extracted function + + return &analysis.SuggestedFix{ + TextEdits: []analysis.TextEdit{{ + Pos: outer.Pos(), + End: outer.End(), + NewText: []byte(fullReplacement.String()), + }}, + }, nil +} + +// adjustRangeForCommentsAndWhiteSpace adjusts the given range to exclude unnecessary leading or +// trailing whitespace characters from selection as well as leading or trailing comments. +// In the following example, each line of the if statement is indented once. There are also two +// extra spaces after the sclosing bracket before the line break and a comment. +// +// \tif (true) { +// \t _ = 1 +// \t} // hello \n +// +// By default, a valid range begins at 'if' and ends at the first whitespace character +// after the '}'. But, users are likely to highlight full lines rather than adjusting +// their cursors for whitespace. To support this use case, we must manually adjust the +// ranges to match the correct AST node. In this particular example, we would adjust +// rng.Start forward to the start of 'if' and rng.End backward to after '}'. +func adjustRangeForCommentsAndWhiteSpace(rng span.Range, tok *token.File, content []byte, file *ast.File) (span.Range, error) { + // Adjust the end of the range to after leading whitespace and comments. + prevStart, start := token.NoPos, rng.Start + startComment := sort.Search(len(file.Comments), func(i int) bool { + // Find the index for the first comment that ends after range start. + return file.Comments[i].End() > rng.Start + }) + for prevStart != start { + prevStart = start + // If start is within a comment, move start to the end + // of the comment group. + if startComment < len(file.Comments) && file.Comments[startComment].Pos() <= start && start < file.Comments[startComment].End() { + start = file.Comments[startComment].End() + startComment++ + } + // Move forwards to find a non-whitespace character. + offset, err := safetoken.Offset(tok, start) + if err != nil { + return span.Range{}, err + } + for offset < len(content) && isGoWhiteSpace(content[offset]) { + offset++ + } + start = tok.Pos(offset) + } + + // Adjust the end of the range to before trailing whitespace and comments. + prevEnd, end := token.NoPos, rng.End + endComment := sort.Search(len(file.Comments), func(i int) bool { + // Find the index for the first comment that ends after the range end. + return file.Comments[i].End() >= rng.End + }) + // Search will return n if not found, so we need to adjust if there are no + // comments that would match. + if endComment == len(file.Comments) { + endComment = -1 + } + for prevEnd != end { + prevEnd = end + // If end is within a comment, move end to the start + // of the comment group. + if endComment >= 0 && file.Comments[endComment].Pos() < end && end <= file.Comments[endComment].End() { + end = file.Comments[endComment].Pos() + endComment-- + } + // Move backwards to find a non-whitespace character. + offset, err := safetoken.Offset(tok, end) + if err != nil { + return span.Range{}, err + } + for offset > 0 && isGoWhiteSpace(content[offset-1]) { + offset-- + } + end = tok.Pos(offset) + } + + return span.NewRange(tok, start, end), nil +} + +// isGoWhiteSpace returns true if b is a considered white space in +// Go as defined by scanner.GoWhitespace. +func isGoWhiteSpace(b byte) bool { + return uint64(scanner.GoWhitespace)&(1< not free + } + return obj, true + } + // sel returns non-nil if n denotes a selection o.x.y that is referenced by the + // span and defined either within the span or in the lexical environment. The bool + // return value acts as an indicator for where it was defined. + var sel func(n *ast.SelectorExpr) (types.Object, bool) + sel = func(n *ast.SelectorExpr) (types.Object, bool) { + switch x := astutil.Unparen(n.X).(type) { + case *ast.SelectorExpr: + return sel(x) + case *ast.Ident: + return id(x) + } + return nil, false + } + seen := make(map[types.Object]*variable) + firstUseIn := make(map[types.Object]token.Pos) + var vars []types.Object + ast.Inspect(node, func(n ast.Node) bool { + if n == nil { + return false + } + if rng.Start <= n.Pos() && n.End() <= rng.End { + var obj types.Object + var isFree, prune bool + switch n := n.(type) { + case *ast.Ident: + obj, isFree = id(n) + case *ast.SelectorExpr: + obj, isFree = sel(n) + prune = true + } + if obj != nil { + seen[obj] = &variable{ + obj: obj, + free: isFree, + } + vars = append(vars, obj) + // Find the first time that the object is used in the selection. + first, ok := firstUseIn[obj] + if !ok || n.Pos() < first { + firstUseIn[obj] = n.Pos() + } + if prune { + return false + } + } + } + return n.Pos() <= rng.End + }) + + // Find identifiers that are initialized or whose values are altered at some + // point in the selected block. For example, in a selected block from lines 2-4, + // variables x, y, and z are included in assigned. However, in a selected block + // from lines 3-4, only variables y and z are included in assigned. + // + // 1: var a int + // 2: var x int + // 3: y := 3 + // 4: z := x + a + // + ast.Inspect(node, func(n ast.Node) bool { + if n == nil { + return false + } + if n.Pos() < rng.Start || n.End() > rng.End { + return n.Pos() <= rng.End + } + switch n := n.(type) { + case *ast.AssignStmt: + for _, assignment := range n.Lhs { + lhs, ok := assignment.(*ast.Ident) + if !ok { + continue + } + obj, _ := id(lhs) + if obj == nil { + continue + } + if _, ok := seen[obj]; !ok { + continue + } + seen[obj].assigned = true + if n.Tok != token.DEFINE { + continue + } + // Find identifiers that are defined prior to being used + // elsewhere in the selection. + // TODO: Include identifiers that are assigned prior to being + // used elsewhere in the selection. Then, change the assignment + // to a definition in the extracted function. + if firstUseIn[obj] != lhs.Pos() { + continue + } + // Ensure that the object is not used in its own re-definition. + // For example: + // var f float64 + // f, e := math.Frexp(f) + for _, expr := range n.Rhs { + if referencesObj(info, expr, obj) { + continue + } + if _, ok := seen[obj]; !ok { + continue + } + seen[obj].defined = true + break + } + } + return false + case *ast.DeclStmt: + gen, ok := n.Decl.(*ast.GenDecl) + if !ok { + return false + } + for _, spec := range gen.Specs { + vSpecs, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + for _, vSpec := range vSpecs.Names { + obj, _ := id(vSpec) + if obj == nil { + continue + } + if _, ok := seen[obj]; !ok { + continue + } + seen[obj].assigned = true + } + } + return false + case *ast.IncDecStmt: + if ident, ok := n.X.(*ast.Ident); !ok { + return false + } else if obj, _ := id(ident); obj == nil { + return false + } else { + if _, ok := seen[obj]; !ok { + return false + } + seen[obj].assigned = true + } + } + return true + }) + var variables []*variable + for _, obj := range vars { + v, ok := seen[obj] + if !ok { + return nil, fmt.Errorf("no seen types.Object for %v", obj) + } + variables = append(variables, v) + } + return variables, nil +} + +// referencesObj checks whether the given object appears in the given expression. +func referencesObj(info *types.Info, expr ast.Expr, obj types.Object) bool { + var hasObj bool + ast.Inspect(expr, func(n ast.Node) bool { + if n == nil { + return false + } + ident, ok := n.(*ast.Ident) + if !ok { + return true + } + objUse := info.Uses[ident] + if obj == objUse { + hasObj = true + return false + } + return false + }) + return hasObj +} + +type fnExtractParams struct { + tok *token.File + path []ast.Node + rng span.Range + outer *ast.FuncDecl + start ast.Node +} + +// CanExtractFunction reports whether the code in the given range can be +// extracted to a function. +func CanExtractFunction(tok *token.File, rng span.Range, src []byte, file *ast.File) (*fnExtractParams, bool, bool, error) { + if rng.Start == rng.End { + return nil, false, false, fmt.Errorf("start and end are equal") + } + var err error + rng, err = adjustRangeForCommentsAndWhiteSpace(rng, tok, src, file) + if err != nil { + return nil, false, false, err + } + path, _ := astutil.PathEnclosingInterval(file, rng.Start, rng.End) + if len(path) == 0 { + return nil, false, false, fmt.Errorf("no path enclosing interval") + } + // Node that encloses the selection must be a statement. + // TODO: Support function extraction for an expression. + _, ok := path[0].(ast.Stmt) + if !ok { + return nil, false, false, fmt.Errorf("node is not a statement") + } + + // Find the function declaration that encloses the selection. + var outer *ast.FuncDecl + for _, p := range path { + if p, ok := p.(*ast.FuncDecl); ok { + outer = p + break + } + } + if outer == nil { + return nil, false, false, fmt.Errorf("no enclosing function") + } + + // Find the nodes at the start and end of the selection. + var start, end ast.Node + ast.Inspect(outer, func(n ast.Node) bool { + if n == nil { + return false + } + // Do not override 'start' with a node that begins at the same location + // but is nested further from 'outer'. + if start == nil && n.Pos() == rng.Start && n.End() <= rng.End { + start = n + } + if end == nil && n.End() == rng.End && n.Pos() >= rng.Start { + end = n + } + return n.Pos() <= rng.End + }) + if start == nil || end == nil { + return nil, false, false, fmt.Errorf("range does not map to AST nodes") + } + // If the region is a blockStmt, use the first and last nodes in the block + // statement. + // { ... } => { ... } + if blockStmt, ok := start.(*ast.BlockStmt); ok { + if len(blockStmt.List) == 0 { + return nil, false, false, fmt.Errorf("range maps to empty block statement") + } + start, end = blockStmt.List[0], blockStmt.List[len(blockStmt.List)-1] + rng.Start, rng.End = start.Pos(), end.End() + } + return &fnExtractParams{ + tok: tok, + path: path, + rng: rng, + outer: outer, + start: start, + }, true, outer.Recv != nil, nil +} + +// objUsed checks if the object is used within the range. It returns the first +// occurrence of the object in the range, if it exists. +func objUsed(info *types.Info, rng span.Range, obj types.Object) (bool, *ast.Ident) { + var firstUse *ast.Ident + for id, objUse := range info.Uses { + if obj != objUse { + continue + } + if id.Pos() < rng.Start || id.End() > rng.End { + continue + } + if firstUse == nil || id.Pos() < firstUse.Pos() { + firstUse = id + } + } + return firstUse != nil, firstUse +} + +// varOverridden traverses the given AST node until we find the given identifier. Then, we +// examine the occurrence of the given identifier and check for (1) whether the identifier +// is being redefined. If the identifier is free, we also check for (2) whether the identifier +// is being reassigned. We will not include an identifier in the return statement of the +// extracted function if it meets one of the above conditions. +func varOverridden(info *types.Info, firstUse *ast.Ident, obj types.Object, isFree bool, node ast.Node) bool { + var isOverriden bool + ast.Inspect(node, func(n ast.Node) bool { + if n == nil { + return false + } + assignment, ok := n.(*ast.AssignStmt) + if !ok { + return true + } + // A free variable is initialized prior to the selection. We can always reassign + // this variable after the selection because it has already been defined. + // Conversely, a non-free variable is initialized within the selection. Thus, we + // cannot reassign this variable after the selection unless it is initialized and + // returned by the extracted function. + if !isFree && assignment.Tok == token.ASSIGN { + return false + } + for _, assigned := range assignment.Lhs { + ident, ok := assigned.(*ast.Ident) + // Check if we found the first use of the identifier. + if !ok || ident != firstUse { + continue + } + objUse := info.Uses[ident] + if objUse == nil || objUse != obj { + continue + } + // Ensure that the object is not used in its own definition. + // For example: + // var f float64 + // f, e := math.Frexp(f) + for _, expr := range assignment.Rhs { + if referencesObj(info, expr, obj) { + return false + } + } + isOverriden = true + return false + } + return false + }) + return isOverriden +} + +// parseBlockStmt generates an AST file from the given text. We then return the portion of the +// file that represents the text. +func parseBlockStmt(fset *token.FileSet, src []byte) (*ast.BlockStmt, error) { + text := "package main\nfunc _() { " + string(src) + " }" + extract, err := parser.ParseFile(fset, "", text, 0) + if err != nil { + return nil, err + } + if len(extract.Decls) == 0 { + return nil, fmt.Errorf("parsed file does not contain any declarations") + } + decl, ok := extract.Decls[0].(*ast.FuncDecl) + if !ok { + return nil, fmt.Errorf("parsed file does not contain expected function declaration") + } + if decl.Body == nil { + return nil, fmt.Errorf("extracted function has no body") + } + return decl.Body, nil +} + +// generateReturnInfo generates the information we need to adjust the return statements and +// signature of the extracted function. We prepare names, signatures, and "zero values" that +// represent the new variables. We also use this information to construct the if statement that +// is inserted below the call to the extracted function. +func generateReturnInfo(enclosing *ast.FuncType, pkg *types.Package, path []ast.Node, file *ast.File, info *types.Info, fset *token.FileSet, pos token.Pos, hasNonNestedReturns bool) ([]*returnVariable, *ast.IfStmt, error) { + var retVars []*returnVariable + var cond *ast.Ident + if !hasNonNestedReturns { + // Generate information for the added bool value. + name, _ := generateAvailableIdentifier(pos, file, path, info, "shouldReturn", 0) + cond = &ast.Ident{Name: name} + retVars = append(retVars, &returnVariable{ + name: cond, + decl: &ast.Field{Type: ast.NewIdent("bool")}, + zeroVal: ast.NewIdent("false"), + }) + } + // Generate information for the values in the return signature of the enclosing function. + if enclosing.Results != nil { + idx := 0 + for _, field := range enclosing.Results.List { + typ := info.TypeOf(field.Type) + if typ == nil { + return nil, nil, fmt.Errorf( + "failed type conversion, AST expression: %T", field.Type) + } + expr := analysisinternal.TypeExpr(file, pkg, typ) + if expr == nil { + return nil, nil, fmt.Errorf("nil AST expression") + } + var name string + name, idx = generateAvailableIdentifier(pos, file, + path, info, "returnValue", idx) + retVars = append(retVars, &returnVariable{ + name: ast.NewIdent(name), + decl: &ast.Field{Type: expr}, + zeroVal: analysisinternal.ZeroValue(file, pkg, typ), + }) + } + } + var ifReturn *ast.IfStmt + if !hasNonNestedReturns { + // Create the return statement for the enclosing function. We must exclude the variable + // for the condition of the if statement (cond) from the return statement. + ifReturn = &ast.IfStmt{ + Cond: cond, + Body: &ast.BlockStmt{ + List: []ast.Stmt{&ast.ReturnStmt{Results: getNames(retVars)[1:]}}, + }, + } + } + return retVars, ifReturn, nil +} + +// adjustReturnStatements adds "zero values" of the given types to each return statement +// in the given AST node. +func adjustReturnStatements(returnTypes []*ast.Field, seenVars map[types.Object]ast.Expr, fset *token.FileSet, file *ast.File, pkg *types.Package, extractedBlock *ast.BlockStmt) error { + var zeroVals []ast.Expr + // Create "zero values" for each type. + for _, returnType := range returnTypes { + var val ast.Expr + for obj, typ := range seenVars { + if typ != returnType.Type { + continue + } + val = analysisinternal.ZeroValue(file, pkg, obj.Type()) + break + } + if val == nil { + return fmt.Errorf( + "could not find matching AST expression for %T", returnType.Type) + } + zeroVals = append(zeroVals, val) + } + // Add "zero values" to each return statement. + // The bool reports whether the enclosing function should return after calling the + // extracted function. We set the bool to 'true' because, if these return statements + // execute, the extracted function terminates early, and the enclosing function must + // return as well. + zeroVals = append(zeroVals, ast.NewIdent("true")) + ast.Inspect(extractedBlock, func(n ast.Node) bool { + if n == nil { + return false + } + if n, ok := n.(*ast.ReturnStmt); ok { + n.Results = append(zeroVals, n.Results...) + return false + } + return true + }) + return nil +} + +// generateFuncCall constructs a call expression for the extracted function, described by the +// given parameters and return variables. +func generateFuncCall(hasNonNestedReturn, hasReturnVals bool, params, returns []ast.Expr, name string, token token.Token, selector string) ast.Node { + var replace ast.Node + callExpr := &ast.CallExpr{ + Fun: ast.NewIdent(name), + Args: params, + } + if selector != "" { + callExpr = &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: ast.NewIdent(selector), + Sel: ast.NewIdent(name), + }, + Args: params, + } + } + if hasReturnVals { + if hasNonNestedReturn { + // Create a return statement that returns the result of the function call. + replace = &ast.ReturnStmt{ + Return: 0, + Results: []ast.Expr{callExpr}, + } + } else { + // Assign the result of the function call. + replace = &ast.AssignStmt{ + Lhs: returns, + Tok: token, + Rhs: []ast.Expr{callExpr}, + } + } + } else { + replace = callExpr + } + return replace +} + +// initializeVars creates variable declarations, if needed. +// Our preference is to replace the selected block with an "x, y, z := fn()" style +// assignment statement. We can use this style when all of the variables in the +// extracted function's return statement are either not defined prior to the extracted block +// or can be safely redefined. However, for example, if z is already defined +// in a different scope, we replace the selected block with: +// +// var x int +// var y string +// x, y, z = fn() +func initializeVars(uninitialized []types.Object, retVars []*returnVariable, seenUninitialized map[types.Object]struct{}, seenVars map[types.Object]ast.Expr) []ast.Stmt { + var declarations []ast.Stmt + for _, obj := range uninitialized { + if _, ok := seenUninitialized[obj]; ok { + continue + } + seenUninitialized[obj] = struct{}{} + valSpec := &ast.ValueSpec{ + Names: []*ast.Ident{ast.NewIdent(obj.Name())}, + Type: seenVars[obj], + } + genDecl := &ast.GenDecl{ + Tok: token.VAR, + Specs: []ast.Spec{valSpec}, + } + declarations = append(declarations, &ast.DeclStmt{Decl: genDecl}) + } + // Each variable added from a return statement in the selection + // must be initialized. + for i, retVar := range retVars { + n := retVar.name.(*ast.Ident) + valSpec := &ast.ValueSpec{ + Names: []*ast.Ident{n}, + Type: retVars[i].decl.Type, + } + genDecl := &ast.GenDecl{ + Tok: token.VAR, + Specs: []ast.Spec{valSpec}, + } + declarations = append(declarations, &ast.DeclStmt{Decl: genDecl}) + } + return declarations +} + +// getNames returns the names from the given list of returnVariable. +func getNames(retVars []*returnVariable) []ast.Expr { + var names []ast.Expr + for _, retVar := range retVars { + names = append(names, retVar.name) + } + return names +} + +// getZeroVals returns the "zero values" from the given list of returnVariable. +func getZeroVals(retVars []*returnVariable) []ast.Expr { + var zvs []ast.Expr + for _, retVar := range retVars { + zvs = append(zvs, retVar.zeroVal) + } + return zvs +} + +// getDecls returns the declarations from the given list of returnVariable. +func getDecls(retVars []*returnVariable) []*ast.Field { + var decls []*ast.Field + for _, retVar := range retVars { + decls = append(decls, retVar.decl) + } + return decls +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/fix.go b/gopls/golang_org_x_tools_gopls/lsp/source/fix.go new file mode 100644 index 0000000..22a38b6 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/fix.go @@ -0,0 +1,144 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "fmt" + "go/ast" + "go/token" + "go/types" + + "golang.org/x/tools/go/analysis" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/fillstruct" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/undeclaredname" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/bug" +) + +type ( + // SuggestedFixFunc is a function used to get the suggested fixes for a given + // gopls command, some of which are provided by go/analysis.Analyzers. Some of + // the analyzers in internal/lsp/analysis are not efficient enough to include + // suggested fixes with their diagnostics, so we have to compute them + // separately. Such analyzers should provide a function with a signature of + // SuggestedFixFunc. + SuggestedFixFunc func(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) + singleFileFixFunc func(fset *token.FileSet, rng span.Range, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) +) + +const ( + FillStruct = "fill_struct" + StubMethods = "stub_methods" + UndeclaredName = "undeclared_name" + ExtractVariable = "extract_variable" + ExtractFunction = "extract_function" + ExtractMethod = "extract_method" +) + +// suggestedFixes maps a suggested fix command id to its handler. +var suggestedFixes = map[string]SuggestedFixFunc{ + FillStruct: singleFile(fillstruct.SuggestedFix), + UndeclaredName: singleFile(undeclaredname.SuggestedFix), + ExtractVariable: singleFile(extractVariable), + ExtractFunction: singleFile(extractFunction), + ExtractMethod: singleFile(extractMethod), + StubMethods: stubSuggestedFixFunc, +} + +// singleFile calls analyzers that expect inputs for a single file +func singleFile(sf singleFileFixFunc) SuggestedFixFunc { + return func(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) { + fset, rng, src, file, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng) + if err != nil { + return nil, nil, err + } + fix, err := sf(fset, rng, src, file, pkg, info) + return fset, fix, err + } +} + +func SuggestedFixFromCommand(cmd protocol.Command, kind protocol.CodeActionKind) SuggestedFix { + return SuggestedFix{ + Title: cmd.Title, + Command: &cmd, + ActionKind: kind, + } +} + +// ApplyFix applies the command's suggested fix to the given file and +// range, returning the resulting edits. +func ApplyFix(ctx context.Context, fix string, snapshot Snapshot, fh VersionedFileHandle, pRng protocol.Range) ([]protocol.TextDocumentEdit, error) { + handler, ok := suggestedFixes[fix] + if !ok { + return nil, fmt.Errorf("no suggested fix function for %s", fix) + } + fset, suggestion, err := handler(ctx, snapshot, fh, pRng) + if err != nil { + return nil, err + } + if suggestion == nil { + return nil, nil + } + editsPerFile := map[span.URI]*protocol.TextDocumentEdit{} + for _, edit := range suggestion.TextEdits { + tokFile := fset.File(edit.Pos) + if tokFile == nil { + return nil, bug.Errorf("no file for edit position") + } + end := edit.End + if !end.IsValid() { + end = edit.Pos + } + fh, err := snapshot.GetVersionedFile(ctx, span.URIFromPath(tokFile.Name())) + if err != nil { + return nil, err + } + te, ok := editsPerFile[fh.URI()] + if !ok { + te = &protocol.TextDocumentEdit{ + TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{ + Version: fh.Version(), + TextDocumentIdentifier: protocol.TextDocumentIdentifier{ + URI: protocol.URIFromSpanURI(fh.URI()), + }, + }, + } + editsPerFile[fh.URI()] = te + } + _, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return nil, err + } + rng, err := pgf.Mapper.PosRange(edit.Pos, end) + if err != nil { + return nil, err + } + te.Edits = append(te.Edits, protocol.TextEdit{ + Range: rng, + NewText: string(edit.NewText), + }) + } + var edits []protocol.TextDocumentEdit + for _, edit := range editsPerFile { + edits = append(edits, *edit) + } + return edits, nil +} + +// getAllSuggestedFixInputs is a helper function to collect all possible needed +// inputs for an AppliesFunc or SuggestedFixFunc. +func getAllSuggestedFixInputs(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, span.Range, []byte, *ast.File, *types.Package, *types.Info, error) { + pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return nil, span.Range{}, nil, nil, nil, nil, fmt.Errorf("getting file for Identifier: %w", err) + } + rng, err := pgf.Mapper.RangeToSpanRange(pRng) + if err != nil { + return nil, span.Range{}, nil, nil, nil, nil, err + } + return pkg.FileSet(), rng, pgf.Src, pgf.File, pkg.GetTypes(), pkg.GetTypesInfo(), nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/folding_range.go b/gopls/golang_org_x_tools_gopls/lsp/source/folding_range.go new file mode 100644 index 0000000..84aaf17 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/folding_range.go @@ -0,0 +1,183 @@ +// 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 source + +import ( + "context" + "go/ast" + "go/token" + "sort" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +// FoldingRangeInfo holds range and kind info of folding for an ast.Node +type FoldingRangeInfo struct { + MappedRange + Kind protocol.FoldingRangeKind +} + +// FoldingRange gets all of the folding range for f. +func FoldingRange(ctx context.Context, snapshot Snapshot, fh FileHandle, lineFoldingOnly bool) (ranges []*FoldingRangeInfo, err error) { + // TODO(suzmue): consider limiting the number of folding ranges returned, and + // implement a way to prioritize folding ranges in that case. + pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) + if err != nil { + return nil, err + } + + // With parse errors, we wouldn't be able to produce accurate folding info. + // LSP protocol (3.16) currently does not have a way to handle this case + // (https://github.com/microsoft/language-server-protocol/issues/1200). + // We cannot return an error either because we are afraid some editors + // may not handle errors nicely. As a workaround, we now return an empty + // result and let the client handle this case by double check the file + // contents (i.e. if the file is not empty and the folding range result + // is empty, raise an internal error). + if pgf.ParseErr != nil { + return nil, nil + } + + // Get folding ranges for comments separately as they are not walked by ast.Inspect. + ranges = append(ranges, commentsFoldingRange(pgf.Tok, pgf.Mapper, pgf.File)...) + + visit := func(n ast.Node) bool { + rng := foldingRangeFunc(pgf.Tok, pgf.Mapper, n, lineFoldingOnly) + if rng != nil { + ranges = append(ranges, rng) + } + return true + } + // Walk the ast and collect folding ranges. + ast.Inspect(pgf.File, visit) + + sort.Slice(ranges, func(i, j int) bool { + irng, _ := ranges[i].Range() + jrng, _ := ranges[j].Range() + return protocol.CompareRange(irng, jrng) < 0 + }) + + return ranges, nil +} + +// foldingRangeFunc calculates the line folding range for ast.Node n +func foldingRangeFunc(tokFile *token.File, m *protocol.ColumnMapper, n ast.Node, lineFoldingOnly bool) *FoldingRangeInfo { + // TODO(suzmue): include trailing empty lines before the closing + // parenthesis/brace. + var kind protocol.FoldingRangeKind + var start, end token.Pos + switch n := n.(type) { + case *ast.BlockStmt: + // Fold between positions of or lines between "{" and "}". + var startList, endList token.Pos + if num := len(n.List); num != 0 { + startList, endList = n.List[0].Pos(), n.List[num-1].End() + } + start, end = validLineFoldingRange(tokFile, n.Lbrace, n.Rbrace, startList, endList, lineFoldingOnly) + case *ast.CaseClause: + // Fold from position of ":" to end. + start, end = n.Colon+1, n.End() + case *ast.CommClause: + // Fold from position of ":" to end. + start, end = n.Colon+1, n.End() + case *ast.CallExpr: + // Fold from position of "(" to position of ")". + start, end = n.Lparen+1, n.Rparen + case *ast.FieldList: + // Fold between positions of or lines between opening parenthesis/brace and closing parenthesis/brace. + var startList, endList token.Pos + if num := len(n.List); num != 0 { + startList, endList = n.List[0].Pos(), n.List[num-1].End() + } + start, end = validLineFoldingRange(tokFile, n.Opening, n.Closing, startList, endList, lineFoldingOnly) + case *ast.GenDecl: + // If this is an import declaration, set the kind to be protocol.Imports. + if n.Tok == token.IMPORT { + kind = protocol.Imports + } + // Fold between positions of or lines between "(" and ")". + var startSpecs, endSpecs token.Pos + if num := len(n.Specs); num != 0 { + startSpecs, endSpecs = n.Specs[0].Pos(), n.Specs[num-1].End() + } + start, end = validLineFoldingRange(tokFile, n.Lparen, n.Rparen, startSpecs, endSpecs, lineFoldingOnly) + case *ast.BasicLit: + // Fold raw string literals from position of "`" to position of "`". + if n.Kind == token.STRING && len(n.Value) >= 2 && n.Value[0] == '`' && n.Value[len(n.Value)-1] == '`' { + start, end = n.Pos(), n.End() + } + case *ast.CompositeLit: + // Fold between positions of or lines between "{" and "}". + var startElts, endElts token.Pos + if num := len(n.Elts); num != 0 { + startElts, endElts = n.Elts[0].Pos(), n.Elts[num-1].End() + } + start, end = validLineFoldingRange(tokFile, n.Lbrace, n.Rbrace, startElts, endElts, lineFoldingOnly) + } + + // Check that folding positions are valid. + if !start.IsValid() || !end.IsValid() { + return nil + } + // in line folding mode, do not fold if the start and end lines are the same. + if lineFoldingOnly && tokFile.Line(start) == tokFile.Line(end) { + return nil + } + return &FoldingRangeInfo{ + MappedRange: NewMappedRange(tokFile, m, start, end), + Kind: kind, + } +} + +// validLineFoldingRange returns start and end token.Pos for folding range if the range is valid. +// returns token.NoPos otherwise, which fails token.IsValid check +func validLineFoldingRange(tokFile *token.File, open, close, start, end token.Pos, lineFoldingOnly bool) (token.Pos, token.Pos) { + if lineFoldingOnly { + if !open.IsValid() || !close.IsValid() { + return token.NoPos, token.NoPos + } + + // Don't want to fold if the start/end is on the same line as the open/close + // as an example, the example below should *not* fold: + // var x = [2]string{"d", + // "e" } + if tokFile.Line(open) == tokFile.Line(start) || + tokFile.Line(close) == tokFile.Line(end) { + return token.NoPos, token.NoPos + } + + return open + 1, end + } + return open + 1, close +} + +// commentsFoldingRange returns the folding ranges for all comment blocks in file. +// The folding range starts at the end of the first line of the comment block, and ends at the end of the +// comment block and has kind protocol.Comment. +func commentsFoldingRange(tokFile *token.File, m *protocol.ColumnMapper, file *ast.File) (comments []*FoldingRangeInfo) { + for _, commentGrp := range file.Comments { + startGrpLine, endGrpLine := tokFile.Line(commentGrp.Pos()), tokFile.Line(commentGrp.End()) + if startGrpLine == endGrpLine { + // Don't fold single line comments. + continue + } + + firstComment := commentGrp.List[0] + startPos, endLinePos := firstComment.Pos(), firstComment.End() + startCmmntLine, endCmmntLine := tokFile.Line(startPos), tokFile.Line(endLinePos) + if startCmmntLine != endCmmntLine { + // If the first comment spans multiple lines, then we want to have the + // folding range start at the end of the first line. + endLinePos = token.Pos(int(startPos) + len(strings.Split(firstComment.Text, "\n")[0])) + } + comments = append(comments, &FoldingRangeInfo{ + // Fold from the end of the first line comment to the end of the comment block. + MappedRange: NewMappedRange(tokFile, m, endLinePos, commentGrp.End()), + Kind: protocol.Comment, + }) + } + return comments +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/format.go b/gopls/golang_org_x_tools_gopls/lsp/source/format.go new file mode 100644 index 0000000..06c2722 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/format.go @@ -0,0 +1,392 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package source provides core features for use by Go editors and tools. +package source + +import ( + "bytes" + "context" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "strings" + "text/scanner" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/lsppos" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/diff" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/imports" +) + +// Format formats a file with a given range. +func Format(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.TextEdit, error) { + ctx, done := event.Start(ctx, "source.Format") + defer done() + + // Generated files shouldn't be edited. So, don't format them + if IsGenerated(ctx, snapshot, fh.URI()) { + return nil, fmt.Errorf("can't format %q: file is generated", fh.URI().Filename()) + } + + fset := snapshot.FileSet() + pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) + if err != nil { + return nil, err + } + // Even if this file has parse errors, it might still be possible to format it. + // Using format.Node on an AST with errors may result in code being modified. + // Attempt to format the source of this file instead. + if pgf.ParseErr != nil { + formatted, err := formatSource(ctx, fh) + if err != nil { + return nil, err + } + return computeTextEdits(ctx, snapshot, pgf, string(formatted)) + } + + // format.Node changes slightly from one release to another, so the version + // of Go used to build the LSP server will determine how it formats code. + // This should be acceptable for all users, who likely be prompted to rebuild + // the LSP server on each Go release. + buf := &bytes.Buffer{} + if err := format.Node(buf, fset, pgf.File); err != nil { + return nil, err + } + formatted := buf.String() + + // Apply additional formatting, if any is supported. Currently, the only + // supported additional formatter is gofumpt. + if format := snapshot.View().Options().GofumptFormat; snapshot.View().Options().Gofumpt && format != nil { + // gofumpt can customize formatting based on language version and module + // path, if available. + // + // Try to derive this information, but fall-back on the default behavior. + // + // TODO: under which circumstances can we fail to find module information? + // Can this, for example, result in inconsistent formatting across saves, + // due to pending calls to packages.Load? + var langVersion, modulePath string + mds, err := snapshot.MetadataForFile(ctx, fh.URI()) + if err == nil && len(mds) > 0 { + if mi := mds[0].ModuleInfo(); mi != nil { + langVersion = mi.GoVersion + modulePath = mi.Path + } + } + b, err := format(ctx, langVersion, modulePath, buf.Bytes()) + if err != nil { + return nil, err + } + formatted = string(b) + } + return computeTextEdits(ctx, snapshot, pgf, formatted) +} + +func formatSource(ctx context.Context, fh FileHandle) ([]byte, error) { + _, done := event.Start(ctx, "source.formatSource") + defer done() + + data, err := fh.Read() + if err != nil { + return nil, err + } + return format.Source(data) +} + +type ImportFix struct { + Fix *imports.ImportFix + Edits []protocol.TextEdit +} + +// AllImportsFixes formats f for each possible fix to the imports. +// In addition to returning the result of applying all edits, +// it returns a list of fixes that could be applied to the file, with the +// corresponding TextEdits that would be needed to apply that fix. +func AllImportsFixes(ctx context.Context, snapshot Snapshot, fh FileHandle) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) { + ctx, done := event.Start(ctx, "source.AllImportsFixes") + defer done() + + pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) + if err != nil { + return nil, nil, err + } + if err := snapshot.RunProcessEnvFunc(ctx, func(opts *imports.Options) error { + allFixEdits, editsPerFix, err = computeImportEdits(snapshot, pgf, opts) + return err + }); err != nil { + return nil, nil, fmt.Errorf("AllImportsFixes: %v", err) + } + return allFixEdits, editsPerFix, nil +} + +// computeImportEdits computes a set of edits that perform one or all of the +// necessary import fixes. +func computeImportEdits(snapshot Snapshot, pgf *ParsedGoFile, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) { + filename := pgf.URI.Filename() + + // Build up basic information about the original file. + allFixes, err := imports.FixImports(filename, pgf.Src, options) + if err != nil { + return nil, nil, err + } + + allFixEdits, err = computeFixEdits(snapshot, pgf, options, allFixes) + if err != nil { + return nil, nil, err + } + + // Apply all of the import fixes to the file. + // Add the edits for each fix to the result. + for _, fix := range allFixes { + edits, err := computeFixEdits(snapshot, pgf, options, []*imports.ImportFix{fix}) + if err != nil { + return nil, nil, err + } + editsPerFix = append(editsPerFix, &ImportFix{ + Fix: fix, + Edits: edits, + }) + } + return allFixEdits, editsPerFix, nil +} + +// ComputeOneImportFixEdits returns text edits for a single import fix. +func ComputeOneImportFixEdits(snapshot Snapshot, pgf *ParsedGoFile, fix *imports.ImportFix) ([]protocol.TextEdit, error) { + options := &imports.Options{ + LocalPrefix: snapshot.View().Options().Local, + // Defaults. + AllErrors: true, + Comments: true, + Fragment: true, + FormatOnly: false, + TabIndent: true, + TabWidth: 8, + } + return computeFixEdits(snapshot, pgf, options, []*imports.ImportFix{fix}) +} + +func computeFixEdits(snapshot Snapshot, pgf *ParsedGoFile, options *imports.Options, fixes []*imports.ImportFix) ([]protocol.TextEdit, error) { + // trim the original data to match fixedData + left, err := importPrefix(pgf.Src) + if err != nil { + return nil, err + } + extra := !strings.Contains(left, "\n") // one line may have more than imports + if extra { + left = string(pgf.Src) + } + if len(left) > 0 && left[len(left)-1] != '\n' { + left += "\n" + } + // Apply the fixes and re-parse the file so that we can locate the + // new imports. + flags := parser.ImportsOnly + if extra { + // used all of origData above, use all of it here too + flags = 0 + } + fixedData, err := imports.ApplyFixes(fixes, "", pgf.Src, options, flags) + if err != nil { + return nil, err + } + if fixedData == nil || fixedData[len(fixedData)-1] != '\n' { + fixedData = append(fixedData, '\n') // ApplyFixes may miss the newline, go figure. + } + edits := snapshot.View().Options().ComputeEdits(left, string(fixedData)) + return protocolEditsFromSource([]byte(left), edits, pgf.Mapper.TokFile) +} + +// importPrefix returns the prefix of the given file content through the final +// import statement. If there are no imports, the prefix is the package +// statement and any comment groups below it. +func importPrefix(src []byte) (string, error) { + fset := token.NewFileSet() + // do as little parsing as possible + f, err := parser.ParseFile(fset, "", src, parser.ImportsOnly|parser.ParseComments) + if err != nil { // This can happen if 'package' is misspelled + return "", fmt.Errorf("importPrefix: failed to parse: %s", err) + } + tok := fset.File(f.Pos()) + var importEnd int + for _, d := range f.Decls { + if x, ok := d.(*ast.GenDecl); ok && x.Tok == token.IMPORT { + if e, err := safetoken.Offset(tok, d.End()); err != nil { + return "", fmt.Errorf("importPrefix: %s", err) + } else if e > importEnd { + importEnd = e + } + } + } + + maybeAdjustToLineEnd := func(pos token.Pos, isCommentNode bool) int { + offset, err := safetoken.Offset(tok, pos) + if err != nil { + return -1 + } + + // Don't go past the end of the file. + if offset > len(src) { + offset = len(src) + } + // The go/ast package does not account for different line endings, and + // specifically, in the text of a comment, it will strip out \r\n line + // endings in favor of \n. To account for these differences, we try to + // return a position on the next line whenever possible. + switch line := tok.Line(tok.Pos(offset)); { + case line < tok.LineCount(): + nextLineOffset, err := safetoken.Offset(tok, tok.LineStart(line+1)) + if err != nil { + return -1 + } + // If we found a position that is at the end of a line, move the + // offset to the start of the next line. + if offset+1 == nextLineOffset { + offset = nextLineOffset + } + case isCommentNode, offset+1 == tok.Size(): + // If the last line of the file is a comment, or we are at the end + // of the file, the prefix is the entire file. + offset = len(src) + } + return offset + } + if importEnd == 0 { + pkgEnd := f.Name.End() + importEnd = maybeAdjustToLineEnd(pkgEnd, false) + } + for _, cgroup := range f.Comments { + for _, c := range cgroup.List { + if end, err := safetoken.Offset(tok, c.End()); err != nil { + return "", err + } else if end > importEnd { + startLine := tok.Position(c.Pos()).Line + endLine := tok.Position(c.End()).Line + + // Work around golang/go#41197 by checking if the comment might + // contain "\r", and if so, find the actual end position of the + // comment by scanning the content of the file. + startOffset, err := safetoken.Offset(tok, c.Pos()) + if err != nil { + return "", err + } + if startLine != endLine && bytes.Contains(src[startOffset:], []byte("\r")) { + if commentEnd := scanForCommentEnd(src[startOffset:]); commentEnd > 0 { + end = startOffset + commentEnd + } + } + importEnd = maybeAdjustToLineEnd(tok.Pos(end), true) + } + } + } + if importEnd > len(src) { + importEnd = len(src) + } + return string(src[:importEnd]), nil +} + +// scanForCommentEnd returns the offset of the end of the multi-line comment +// at the start of the given byte slice. +func scanForCommentEnd(src []byte) int { + var s scanner.Scanner + s.Init(bytes.NewReader(src)) + s.Mode ^= scanner.SkipComments + + t := s.Scan() + if t == scanner.Comment { + return s.Pos().Offset + } + return 0 +} + +func computeTextEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, formatted string) ([]protocol.TextEdit, error) { + _, done := event.Start(ctx, "source.computeTextEdits") + defer done() + + edits := snapshot.View().Options().ComputeEdits(string(pgf.Src), formatted) + return ToProtocolEdits(pgf.Mapper, edits) +} + +// protocolEditsFromSource converts text edits to LSP edits using the original +// source. +func protocolEditsFromSource(src []byte, edits []diff.Edit, tf *token.File) ([]protocol.TextEdit, error) { + m := lsppos.NewMapper(src) + var result []protocol.TextEdit + for _, edit := range edits { + rng, err := m.Range(edit.Start, edit.End) + if err != nil { + return nil, err + } + + if rng.Start == rng.End && edit.New == "" { + // Degenerate case, which may result from a diff tool wanting to delete + // '\r' in line endings. Filter it out. + continue + } + result = append(result, protocol.TextEdit{ + Range: rng, + NewText: edit.New, + }) + } + return result, nil +} + +// ToProtocolEdits converts diff.Edits to LSP TextEdits. +// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray +func ToProtocolEdits(m *protocol.ColumnMapper, edits []diff.Edit) ([]protocol.TextEdit, error) { + // LSP doesn't require TextEditArray to be sorted: + // this is the receiver's concern. But govim, and perhaps + // other clients have historically relied on the order. + edits = append([]diff.Edit(nil), edits...) + diff.SortEdits(edits) + + result := make([]protocol.TextEdit, len(edits)) + for i, edit := range edits { + rng, err := m.OffsetRange(edit.Start, edit.End) + if err != nil { + return nil, err + } + result[i] = protocol.TextEdit{ + Range: rng, + NewText: edit.New, + } + } + return result, nil +} + +// FromProtocolEdits converts LSP TextEdits to diff.Edits. +// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray +func FromProtocolEdits(m *protocol.ColumnMapper, edits []protocol.TextEdit) ([]diff.Edit, error) { + if edits == nil { + return nil, nil + } + result := make([]diff.Edit, len(edits)) + for i, edit := range edits { + spn, err := m.RangeSpan(edit.Range) + if err != nil { + return nil, err + } + result[i] = diff.Edit{ + Start: spn.Start().Offset(), + End: spn.End().Offset(), + New: edit.NewText, + } + } + return result, nil +} + +// ApplyProtocolEdits applies the patch (edits) to m.Content and returns the result. +// It also returns the edits converted to diff-package form. +func ApplyProtocolEdits(m *protocol.ColumnMapper, edits []protocol.TextEdit) (string, []diff.Edit, error) { + diffEdits, err := FromProtocolEdits(m, edits) + if err != nil { + return "", nil, err + } + out, err := diff.Apply(string(m.Content), diffEdits) + return out, diffEdits, err +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/gc_annotations.go b/gopls/golang_org_x_tools_gopls/lsp/source/gc_annotations.go new file mode 100644 index 0000000..fed90db --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/gc_annotations.go @@ -0,0 +1,214 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gocommand" +) + +type Annotation string + +const ( + // Nil controls nil checks. + Nil Annotation = "nil" + + // Escape controls diagnostics about escape choices. + Escape Annotation = "escape" + + // Inline controls diagnostics about inlining choices. + Inline Annotation = "inline" + + // Bounds controls bounds checking diagnostics. + Bounds Annotation = "bounds" +) + +func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkg Package) (map[VersionedFileIdentity][]*Diagnostic, error) { + if len(pkg.CompiledGoFiles()) == 0 { + return nil, nil + } + pkgDir := filepath.Dir(pkg.CompiledGoFiles()[0].URI.Filename()) + outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid())) + + if err := os.MkdirAll(outDir, 0700); err != nil { + return nil, err + } + tmpFile, err := ioutil.TempFile(os.TempDir(), "gopls-x") + if err != nil { + return nil, err + } + defer os.Remove(tmpFile.Name()) + + outDirURI := span.URIFromPath(outDir) + // GC details doesn't handle Windows URIs in the form of "file:///C:/...", + // so rewrite them to "file://C:/...". See golang/go#41614. + if !strings.HasPrefix(outDir, "/") { + outDirURI = span.URI(strings.Replace(string(outDirURI), "file:///", "file://", 1)) + } + inv := &gocommand.Invocation{ + Verb: "build", + Args: []string{ + fmt.Sprintf("-gcflags=-json=0,%s", outDirURI), + fmt.Sprintf("-o=%s", tmpFile.Name()), + ".", + }, + WorkingDir: pkgDir, + } + _, err = snapshot.RunGoCommandDirect(ctx, Normal, inv) + if err != nil { + return nil, err + } + files, err := findJSONFiles(outDir) + if err != nil { + return nil, err + } + reports := make(map[VersionedFileIdentity][]*Diagnostic) + opts := snapshot.View().Options() + var parseError error + for _, fn := range files { + uri, diagnostics, err := parseDetailsFile(fn, opts) + if err != nil { + // expect errors for all the files, save 1 + parseError = err + } + fh := snapshot.FindFile(uri) + if fh == nil { + continue + } + if pkgDir != filepath.Dir(fh.URI().Filename()) { + // https://github.com/golang/go/issues/42198 + // sometimes the detail diagnostics generated for files + // outside the package can never be taken back. + continue + } + reports[fh.VersionedFileIdentity()] = diagnostics + } + return reports, parseError +} + +func parseDetailsFile(filename string, options *Options) (span.URI, []*Diagnostic, error) { + buf, err := ioutil.ReadFile(filename) + if err != nil { + return "", nil, err + } + var ( + uri span.URI + i int + diagnostics []*Diagnostic + ) + type metadata struct { + File string `json:"file,omitempty"` + } + for dec := json.NewDecoder(bytes.NewReader(buf)); dec.More(); { + // The first element always contains metadata. + if i == 0 { + i++ + m := new(metadata) + if err := dec.Decode(m); err != nil { + return "", nil, err + } + if !strings.HasSuffix(m.File, ".go") { + continue // + } + uri = span.URIFromPath(m.File) + continue + } + d := new(protocol.Diagnostic) + if err := dec.Decode(d); err != nil { + return "", nil, err + } + msg := d.Code.(string) + if msg != "" { + msg = fmt.Sprintf("%s(%s)", msg, d.Message) + } + if !showDiagnostic(msg, d.Source, options) { + continue + } + var related []RelatedInformation + for _, ri := range d.RelatedInformation { + related = append(related, RelatedInformation{ + URI: ri.Location.URI.SpanURI(), + Range: zeroIndexedRange(ri.Location.Range), + Message: ri.Message, + }) + } + diagnostic := &Diagnostic{ + URI: uri, + Range: zeroIndexedRange(d.Range), + Message: msg, + Severity: d.Severity, + Source: OptimizationDetailsError, // d.Source is always "go compiler" as of 1.16, use our own + Tags: d.Tags, + Related: related, + } + diagnostics = append(diagnostics, diagnostic) + i++ + } + return uri, diagnostics, nil +} + +// showDiagnostic reports whether a given diagnostic should be shown to the end +// user, given the current options. +func showDiagnostic(msg, source string, o *Options) bool { + if source != "go compiler" { + return false + } + if o.Annotations == nil { + return true + } + switch { + case strings.HasPrefix(msg, "canInline") || + strings.HasPrefix(msg, "cannotInline") || + strings.HasPrefix(msg, "inlineCall"): + return o.Annotations[Inline] + case strings.HasPrefix(msg, "escape") || msg == "leak": + return o.Annotations[Escape] + case strings.HasPrefix(msg, "nilcheck"): + return o.Annotations[Nil] + case strings.HasPrefix(msg, "isInBounds") || + strings.HasPrefix(msg, "isSliceInBounds"): + return o.Annotations[Bounds] + } + return false +} + +// The range produced by the compiler is 1-indexed, so subtract range by 1. +func zeroIndexedRange(rng protocol.Range) protocol.Range { + return protocol.Range{ + Start: protocol.Position{ + Line: rng.Start.Line - 1, + Character: rng.Start.Character - 1, + }, + End: protocol.Position{ + Line: rng.End.Line - 1, + Character: rng.End.Character - 1, + }, + } +} + +func findJSONFiles(dir string) ([]string, error) { + ans := []string{} + f := func(path string, fi os.FileInfo, _ error) error { + if fi.IsDir() { + return nil + } + if strings.HasSuffix(path, ".json") { + ans = append(ans, path) + } + return nil + } + err := filepath.Walk(dir, f) + return ans, err +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/highlight.go b/gopls/golang_org_x_tools_gopls/lsp/source/highlight.go new file mode 100644 index 0000000..a89fdb2 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/highlight.go @@ -0,0 +1,504 @@ +// 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 source + +import ( + "context" + "fmt" + "go/ast" + "go/token" + "go/types" + "strings" + + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +func Highlight(ctx context.Context, snapshot Snapshot, fh FileHandle, position protocol.Position) ([]protocol.Range, error) { + ctx, done := event.Start(ctx, "source.Highlight") + defer done() + + // Don't use GetParsedFile because it uses TypecheckWorkspace, and we + // always want fully parsed files for highlight, regardless of whether + // the file belongs to a workspace package. + pkg, err := snapshot.PackageForFile(ctx, fh.URI(), TypecheckFull, WidestPackage) + if err != nil { + return nil, fmt.Errorf("getting package for Highlight: %w", err) + } + pgf, err := pkg.File(fh.URI()) + if err != nil { + return nil, fmt.Errorf("getting file for Highlight: %w", err) + } + + pos, err := pgf.Mapper.Pos(position) + if err != nil { + return nil, err + } + path, _ := astutil.PathEnclosingInterval(pgf.File, pos, pos) + if len(path) == 0 { + return nil, fmt.Errorf("no enclosing position found for %v:%v", position.Line, position.Character) + } + // If start == end for astutil.PathEnclosingInterval, the 1-char interval + // following start is used instead. As a result, we might not get an exact + // match so we should check the 1-char interval to the left of the passed + // in position to see if that is an exact match. + if _, ok := path[0].(*ast.Ident); !ok { + if p, _ := astutil.PathEnclosingInterval(pgf.File, pos-1, pos-1); p != nil { + switch p[0].(type) { + case *ast.Ident, *ast.SelectorExpr: + path = p // use preceding ident/selector + } + } + } + result, err := highlightPath(pkg, path) + if err != nil { + return nil, err + } + var ranges []protocol.Range + for rng := range result { + mRng, err := posToMappedRange(pkg, rng.start, rng.end) + if err != nil { + return nil, err + } + pRng, err := mRng.Range() + if err != nil { + return nil, err + } + ranges = append(ranges, pRng) + } + return ranges, nil +} + +func highlightPath(pkg Package, path []ast.Node) (map[posRange]struct{}, error) { + result := make(map[posRange]struct{}) + switch node := path[0].(type) { + case *ast.BasicLit: + if len(path) > 1 { + if _, ok := path[1].(*ast.ImportSpec); ok { + err := highlightImportUses(pkg, path, result) + return result, err + } + } + highlightFuncControlFlow(path, result) + case *ast.ReturnStmt, *ast.FuncDecl, *ast.FuncType: + highlightFuncControlFlow(path, result) + case *ast.Ident: + highlightIdentifiers(pkg, path, result) + case *ast.ForStmt, *ast.RangeStmt: + highlightLoopControlFlow(path, result) + case *ast.SwitchStmt: + highlightSwitchFlow(path, result) + case *ast.BranchStmt: + // BREAK can exit a loop, switch or select, while CONTINUE exit a loop so + // these need to be handled separately. They can also be embedded in any + // other loop/switch/select if they have a label. TODO: add support for + // GOTO and FALLTHROUGH as well. + if node.Label != nil { + highlightLabeledFlow(node, result) + } else { + switch node.Tok { + case token.BREAK: + highlightUnlabeledBreakFlow(path, result) + case token.CONTINUE: + highlightLoopControlFlow(path, result) + } + } + default: + // If the cursor is in an unidentified area, return empty results. + return nil, nil + } + return result, nil +} + +type posRange struct { + start, end token.Pos +} + +func highlightFuncControlFlow(path []ast.Node, result map[posRange]struct{}) { + var enclosingFunc ast.Node + var returnStmt *ast.ReturnStmt + var resultsList *ast.FieldList + inReturnList := false + +Outer: + // Reverse walk the path till we get to the func block. + for i, n := range path { + switch node := n.(type) { + case *ast.KeyValueExpr: + // If cursor is in a key: value expr, we don't want control flow highlighting + return + case *ast.CallExpr: + // If cursor is an arg in a callExpr, we don't want control flow highlighting. + if i > 0 { + for _, arg := range node.Args { + if arg == path[i-1] { + return + } + } + } + case *ast.Field: + inReturnList = true + case *ast.FuncLit: + enclosingFunc = n + resultsList = node.Type.Results + break Outer + case *ast.FuncDecl: + enclosingFunc = n + resultsList = node.Type.Results + break Outer + case *ast.ReturnStmt: + returnStmt = node + // If the cursor is not directly in a *ast.ReturnStmt, then + // we need to know if it is within one of the values that is being returned. + inReturnList = inReturnList || path[0] != returnStmt + } + } + // Cursor is not in a function. + if enclosingFunc == nil { + return + } + // If the cursor is on a "return" or "func" keyword, we should highlight all of the exit + // points of the function, including the "return" and "func" keywords. + highlightAllReturnsAndFunc := path[0] == returnStmt || path[0] == enclosingFunc + switch path[0].(type) { + case *ast.Ident, *ast.BasicLit: + // Cursor is in an identifier and not in a return statement or in the results list. + if returnStmt == nil && !inReturnList { + return + } + case *ast.FuncType: + highlightAllReturnsAndFunc = true + } + // The user's cursor may be within the return statement of a function, + // or within the result section of a function's signature. + // index := -1 + var nodes []ast.Node + if returnStmt != nil { + for _, n := range returnStmt.Results { + nodes = append(nodes, n) + } + } else if resultsList != nil { + for _, n := range resultsList.List { + nodes = append(nodes, n) + } + } + _, index := nodeAtPos(nodes, path[0].Pos()) + + // Highlight the correct argument in the function declaration return types. + if resultsList != nil && -1 < index && index < len(resultsList.List) { + rng := posRange{ + start: resultsList.List[index].Pos(), + end: resultsList.List[index].End(), + } + result[rng] = struct{}{} + } + // Add the "func" part of the func declaration. + if highlightAllReturnsAndFunc { + r := posRange{ + start: enclosingFunc.Pos(), + end: enclosingFunc.Pos() + token.Pos(len("func")), + } + result[r] = struct{}{} + } + ast.Inspect(enclosingFunc, func(n ast.Node) bool { + // Don't traverse any other functions. + switch n.(type) { + case *ast.FuncDecl, *ast.FuncLit: + return enclosingFunc == n + } + ret, ok := n.(*ast.ReturnStmt) + if !ok { + return true + } + var toAdd ast.Node + // Add the entire return statement, applies when highlight the word "return" or "func". + if highlightAllReturnsAndFunc { + toAdd = n + } + // Add the relevant field within the entire return statement. + if -1 < index && index < len(ret.Results) { + toAdd = ret.Results[index] + } + if toAdd != nil { + result[posRange{start: toAdd.Pos(), end: toAdd.End()}] = struct{}{} + } + return false + }) +} + +func highlightUnlabeledBreakFlow(path []ast.Node, result map[posRange]struct{}) { + // Reverse walk the path until we find closest loop, select, or switch. + for _, n := range path { + switch n.(type) { + case *ast.ForStmt, *ast.RangeStmt: + highlightLoopControlFlow(path, result) + return // only highlight the innermost statement + case *ast.SwitchStmt: + highlightSwitchFlow(path, result) + return + case *ast.SelectStmt: + // TODO: add highlight when breaking a select. + return + } + } +} + +func highlightLabeledFlow(node *ast.BranchStmt, result map[posRange]struct{}) { + obj := node.Label.Obj + if obj == nil || obj.Decl == nil { + return + } + label, ok := obj.Decl.(*ast.LabeledStmt) + if !ok { + return + } + switch label.Stmt.(type) { + case *ast.ForStmt, *ast.RangeStmt: + highlightLoopControlFlow([]ast.Node{label.Stmt, label}, result) + case *ast.SwitchStmt: + highlightSwitchFlow([]ast.Node{label.Stmt, label}, result) + } +} + +func labelFor(path []ast.Node) *ast.Ident { + if len(path) > 1 { + if n, ok := path[1].(*ast.LabeledStmt); ok { + return n.Label + } + } + return nil +} + +func highlightLoopControlFlow(path []ast.Node, result map[posRange]struct{}) { + var loop ast.Node + var loopLabel *ast.Ident + stmtLabel := labelFor(path) +Outer: + // Reverse walk the path till we get to the for loop. + for i := range path { + switch n := path[i].(type) { + case *ast.ForStmt, *ast.RangeStmt: + loopLabel = labelFor(path[i:]) + + if stmtLabel == nil || loopLabel == stmtLabel { + loop = n + break Outer + } + } + } + if loop == nil { + return + } + + // Add the for statement. + rng := posRange{ + start: loop.Pos(), + end: loop.Pos() + token.Pos(len("for")), + } + result[rng] = struct{}{} + + // Traverse AST to find branch statements within the same for-loop. + ast.Inspect(loop, func(n ast.Node) bool { + switch n.(type) { + case *ast.ForStmt, *ast.RangeStmt: + return loop == n + case *ast.SwitchStmt, *ast.SelectStmt: + return false + } + b, ok := n.(*ast.BranchStmt) + if !ok { + return true + } + if b.Label == nil || labelDecl(b.Label) == loopLabel { + result[posRange{start: b.Pos(), end: b.End()}] = struct{}{} + } + return true + }) + + // Find continue statements in the same loop or switches/selects. + ast.Inspect(loop, func(n ast.Node) bool { + switch n.(type) { + case *ast.ForStmt, *ast.RangeStmt: + return loop == n + } + + if n, ok := n.(*ast.BranchStmt); ok && n.Tok == token.CONTINUE { + result[posRange{start: n.Pos(), end: n.End()}] = struct{}{} + } + return true + }) + + // We don't need to check other for loops if we aren't looking for labeled statements. + if loopLabel == nil { + return + } + + // Find labeled branch statements in any loop. + ast.Inspect(loop, func(n ast.Node) bool { + b, ok := n.(*ast.BranchStmt) + if !ok { + return true + } + // statement with labels that matches the loop + if b.Label != nil && labelDecl(b.Label) == loopLabel { + result[posRange{start: b.Pos(), end: b.End()}] = struct{}{} + } + return true + }) +} + +func highlightSwitchFlow(path []ast.Node, result map[posRange]struct{}) { + var switchNode ast.Node + var switchNodeLabel *ast.Ident + stmtLabel := labelFor(path) +Outer: + // Reverse walk the path till we get to the switch statement. + for i := range path { + switch n := path[i].(type) { + case *ast.SwitchStmt: + switchNodeLabel = labelFor(path[i:]) + if stmtLabel == nil || switchNodeLabel == stmtLabel { + switchNode = n + break Outer + } + } + } + // Cursor is not in a switch statement + if switchNode == nil { + return + } + + // Add the switch statement. + rng := posRange{ + start: switchNode.Pos(), + end: switchNode.Pos() + token.Pos(len("switch")), + } + result[rng] = struct{}{} + + // Traverse AST to find break statements within the same switch. + ast.Inspect(switchNode, func(n ast.Node) bool { + switch n.(type) { + case *ast.SwitchStmt: + return switchNode == n + case *ast.ForStmt, *ast.RangeStmt, *ast.SelectStmt: + return false + } + + b, ok := n.(*ast.BranchStmt) + if !ok || b.Tok != token.BREAK { + return true + } + + if b.Label == nil || labelDecl(b.Label) == switchNodeLabel { + result[posRange{start: b.Pos(), end: b.End()}] = struct{}{} + } + return true + }) + + // We don't need to check other switches if we aren't looking for labeled statements. + if switchNodeLabel == nil { + return + } + + // Find labeled break statements in any switch + ast.Inspect(switchNode, func(n ast.Node) bool { + b, ok := n.(*ast.BranchStmt) + if !ok || b.Tok != token.BREAK { + return true + } + + if b.Label != nil && labelDecl(b.Label) == switchNodeLabel { + result[posRange{start: b.Pos(), end: b.End()}] = struct{}{} + } + + return true + }) +} + +func labelDecl(n *ast.Ident) *ast.Ident { + if n == nil { + return nil + } + if n.Obj == nil { + return nil + } + if n.Obj.Decl == nil { + return nil + } + stmt, ok := n.Obj.Decl.(*ast.LabeledStmt) + if !ok { + return nil + } + return stmt.Label +} + +func highlightImportUses(pkg Package, path []ast.Node, result map[posRange]struct{}) error { + basicLit, ok := path[0].(*ast.BasicLit) + if !ok { + return fmt.Errorf("highlightImportUses called with an ast.Node of type %T", basicLit) + } + ast.Inspect(path[len(path)-1], func(node ast.Node) bool { + if imp, ok := node.(*ast.ImportSpec); ok && imp.Path == basicLit { + result[posRange{start: node.Pos(), end: node.End()}] = struct{}{} + return false + } + n, ok := node.(*ast.Ident) + if !ok { + return true + } + obj, ok := pkg.GetTypesInfo().ObjectOf(n).(*types.PkgName) + if !ok { + return true + } + if !strings.Contains(basicLit.Value, obj.Name()) { + return true + } + result[posRange{start: n.Pos(), end: n.End()}] = struct{}{} + return false + }) + return nil +} + +func highlightIdentifiers(pkg Package, path []ast.Node, result map[posRange]struct{}) error { + id, ok := path[0].(*ast.Ident) + if !ok { + return fmt.Errorf("highlightIdentifiers called with an ast.Node of type %T", id) + } + // Check if ident is inside return or func decl. + highlightFuncControlFlow(path, result) + + // TODO: maybe check if ident is a reserved word, if true then don't continue and return results. + + idObj := pkg.GetTypesInfo().ObjectOf(id) + pkgObj, isImported := idObj.(*types.PkgName) + ast.Inspect(path[len(path)-1], func(node ast.Node) bool { + if imp, ok := node.(*ast.ImportSpec); ok && isImported { + highlightImport(pkgObj, imp, result) + } + n, ok := node.(*ast.Ident) + if !ok { + return true + } + if n.Name != id.Name { + return false + } + if nObj := pkg.GetTypesInfo().ObjectOf(n); nObj == idObj { + result[posRange{start: n.Pos(), end: n.End()}] = struct{}{} + } + return false + }) + return nil +} + +func highlightImport(obj *types.PkgName, imp *ast.ImportSpec, result map[posRange]struct{}) { + if imp.Name != nil || imp.Path == nil { + return + } + if !strings.Contains(imp.Path.Value, obj.Name()) { + return + } + result[posRange{start: imp.Path.Pos(), end: imp.Path.End()}] = struct{}{} +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/hover.go b/gopls/golang_org_x_tools_gopls/lsp/source/hover.go new file mode 100644 index 0000000..427c189 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/hover.go @@ -0,0 +1,984 @@ +// 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 source + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "go/ast" + "go/constant" + "go/doc" + "go/format" + "go/token" + "go/types" + "strconv" + "strings" + "time" + "unicode/utf8" + + "golang.org/x/text/unicode/runenames" + "golang.org/x/tools/go/types/typeutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/bug" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +// HoverContext contains context extracted from the syntax and type information +// of a given node, for use in various summaries (hover, autocomplete, +// signature help). +type HoverContext struct { + // signatureSource is the object or node use to derive the hover signature. + // + // It may also hold a precomputed string. + // TODO(rfindley): pre-compute all signatures to avoid this indirection. + signatureSource interface{} + + // comment is the most relevant comment group associated with the hovered object. + Comment *ast.CommentGroup +} + +// HoverJSON contains information used by hover. It is also the JSON returned +// for the "structured" hover format +type HoverJSON struct { + // Synopsis is a single sentence synopsis of the symbol's documentation. + Synopsis string `json:"synopsis"` + + // FullDocumentation is the symbol's full documentation. + FullDocumentation string `json:"fullDocumentation"` + + // Signature is the symbol's signature. + Signature string `json:"signature"` + + // SingleLine is a single line describing the symbol. + // This is recommended only for use in clients that show a single line for hover. + SingleLine string `json:"singleLine"` + + // SymbolName is the types.Object.Name for the given symbol. + SymbolName string `json:"symbolName"` + + // LinkPath is the pkg.go.dev link for the given symbol. + // For example, the "go/ast" part of "pkg.go.dev/go/ast#Node". + LinkPath string `json:"linkPath"` + + // LinkAnchor is the pkg.go.dev link anchor for the given symbol. + // For example, the "Node" part of "pkg.go.dev/go/ast#Node". + LinkAnchor string `json:"linkAnchor"` +} + +func Hover(ctx context.Context, snapshot Snapshot, fh FileHandle, position protocol.Position) (*protocol.Hover, error) { + ident, err := Identifier(ctx, snapshot, fh, position) + if err != nil { + if hover, innerErr := hoverRune(ctx, snapshot, fh, position); innerErr == nil { + return hover, nil + } + return nil, nil + } + h, err := HoverIdentifier(ctx, ident) + if err != nil { + return nil, err + } + rng, err := ident.Range() + if err != nil { + return nil, err + } + hover, err := FormatHover(h, snapshot.View().Options()) + if err != nil { + return nil, err + } + return &protocol.Hover{ + Contents: protocol.MarkupContent{ + Kind: snapshot.View().Options().PreferredContentFormat, + Value: hover, + }, + Range: rng, + }, nil +} + +func hoverRune(ctx context.Context, snapshot Snapshot, fh FileHandle, position protocol.Position) (*protocol.Hover, error) { + ctx, done := event.Start(ctx, "source.hoverRune") + defer done() + + r, mrng, err := findRune(ctx, snapshot, fh, position) + if err != nil { + return nil, err + } + rng, err := mrng.Range() + if err != nil { + return nil, err + } + + var desc string + runeName := runenames.Name(r) + if len(runeName) > 0 && runeName[0] == '<' { + // Check if the rune looks like an HTML tag. If so, trim the surrounding <> + // characters to work around https://github.com/microsoft/vscode/issues/124042. + runeName = strings.TrimRight(runeName[1:], ">") + } + if strconv.IsPrint(r) { + desc = fmt.Sprintf("'%s', U+%04X, %s", string(r), uint32(r), runeName) + } else { + desc = fmt.Sprintf("U+%04X, %s", uint32(r), runeName) + } + return &protocol.Hover{ + Contents: protocol.MarkupContent{ + Kind: snapshot.View().Options().PreferredContentFormat, + Value: desc, + }, + Range: rng, + }, nil +} + +// ErrNoRuneFound is the error returned when no rune is found at a particular position. +var ErrNoRuneFound = errors.New("no rune found") + +// findRune returns rune information for a position in a file. +func findRune(ctx context.Context, snapshot Snapshot, fh FileHandle, position protocol.Position) (rune, MappedRange, error) { + pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return 0, MappedRange{}, err + } + pos, err := pgf.Mapper.Pos(position) + if err != nil { + return 0, MappedRange{}, err + } + + // Find the basic literal enclosing the given position, if there is one. + var lit *ast.BasicLit + var found bool + ast.Inspect(pgf.File, func(n ast.Node) bool { + if found { + return false + } + if n, ok := n.(*ast.BasicLit); ok && pos >= n.Pos() && pos <= n.End() { + lit = n + found = true + } + return !found + }) + if !found { + return 0, MappedRange{}, ErrNoRuneFound + } + + var r rune + var start, end token.Pos + switch lit.Kind { + case token.CHAR: + s, err := strconv.Unquote(lit.Value) + if err != nil { + // If the conversion fails, it's because of an invalid syntax, therefore + // there is no rune to be found. + return 0, MappedRange{}, ErrNoRuneFound + } + r, _ = utf8.DecodeRuneInString(s) + if r == utf8.RuneError { + return 0, MappedRange{}, fmt.Errorf("rune error") + } + start, end = lit.Pos(), lit.End() + case token.INT: + // It's an integer, scan only if it is a hex litteral whose bitsize in + // ranging from 8 to 32. + if !(strings.HasPrefix(lit.Value, "0x") && len(lit.Value[2:]) >= 2 && len(lit.Value[2:]) <= 8) { + return 0, MappedRange{}, ErrNoRuneFound + } + v, err := strconv.ParseUint(lit.Value[2:], 16, 32) + if err != nil { + return 0, MappedRange{}, err + } + r = rune(v) + if r == utf8.RuneError { + return 0, MappedRange{}, fmt.Errorf("rune error") + } + start, end = lit.Pos(), lit.End() + case token.STRING: + // It's a string, scan only if it contains a unicode escape sequence under or before the + // current cursor position. + var found bool + litOffset, err := safetoken.Offset(pgf.Tok, lit.Pos()) + if err != nil { + return 0, MappedRange{}, err + } + offset, err := safetoken.Offset(pgf.Tok, pos) + if err != nil { + return 0, MappedRange{}, err + } + for i := offset - litOffset; i > 0; i-- { + // Start at the cursor position and search backward for the beginning of a rune escape sequence. + rr, _ := utf8.DecodeRuneInString(lit.Value[i:]) + if rr == utf8.RuneError { + return 0, MappedRange{}, fmt.Errorf("rune error") + } + if rr == '\\' { + // Got the beginning, decode it. + var tail string + r, _, tail, err = strconv.UnquoteChar(lit.Value[i:], '"') + if err != nil { + // If the conversion fails, it's because of an invalid syntax, therefore is no rune to be found. + return 0, MappedRange{}, ErrNoRuneFound + } + // Only the rune escape sequence part of the string has to be highlighted, recompute the range. + runeLen := len(lit.Value) - (int(i) + len(tail)) + start = token.Pos(int(lit.Pos()) + int(i)) + end = token.Pos(int(start) + runeLen) + found = true + break + } + } + if !found { + // No escape sequence found + return 0, MappedRange{}, ErrNoRuneFound + } + default: + return 0, MappedRange{}, ErrNoRuneFound + } + + mappedRange, err := posToMappedRange(pkg, start, end) + if err != nil { + return 0, MappedRange{}, err + } + return r, mappedRange, nil +} + +func HoverIdentifier(ctx context.Context, i *IdentifierInfo) (*HoverJSON, error) { + ctx, done := event.Start(ctx, "source.Hover") + defer done() + + hoverCtx, err := FindHoverContext(ctx, i.Snapshot, i.pkg, i.Declaration.obj, i.Declaration.node, i.Declaration.fullDecl) + if err != nil { + return nil, err + } + + h := &HoverJSON{ + FullDocumentation: hoverCtx.Comment.Text(), + Synopsis: doc.Synopsis(hoverCtx.Comment.Text()), + } + + fset := i.pkg.FileSet() + // Determine the symbol's signature. + switch x := hoverCtx.signatureSource.(type) { + case string: + h.Signature = x // a pre-computed signature + + case *ast.TypeSpec: + x2 := *x + // Don't duplicate comments when formatting type specs. + x2.Doc = nil + x2.Comment = nil + var b strings.Builder + b.WriteString("type ") + if err := format.Node(&b, fset, &x2); err != nil { + return nil, err + } + + // Display the declared methods accessible from the identifier. + // + // (The format.Node call above displays any struct fields, public + // or private, in syntactic form. We choose not to recursively + // enumerate any fields and methods promoted from them.) + obj := i.Type.Object + if obj != nil && !types.IsInterface(obj.Type()) { + sep := "\n\n" + for _, m := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { + if (m.Obj().Exported() || m.Obj().Pkg() == i.pkg.GetTypes()) && len(m.Index()) == 1 { + b.WriteString(sep) + sep = "\n" + b.WriteString(objectString(m.Obj(), i.qf, nil)) + } + } + } + + h.Signature = b.String() + + case ast.Node: + var b strings.Builder + if err := format.Node(&b, fset, x); err != nil { + return nil, err + } + h.Signature = b.String() + + // Check if the variable is an integer whose value we can present in a more + // user-friendly way, i.e. `var hex = 0xe34e` becomes `var hex = 58190` + if spec, ok := x.(*ast.ValueSpec); ok && len(spec.Values) > 0 { + if lit, ok := spec.Values[0].(*ast.BasicLit); ok && len(spec.Names) > 0 { + val := constant.MakeFromLiteral(types.ExprString(lit), lit.Kind, 0) + h.Signature = fmt.Sprintf("var %s = %s", spec.Names[0], val) + } + } + + case types.Object: + // If the variable is implicitly declared in a type switch, we need to + // manually generate its object string. + if typ := i.Declaration.typeSwitchImplicit; typ != nil { + if v, ok := x.(*types.Var); ok { + h.Signature = fmt.Sprintf("var %s %s", v.Name(), types.TypeString(typ, i.qf)) + break + } + } + h.Signature = objectString(x, i.qf, i.Inferred) + } + if obj := i.Declaration.obj; obj != nil { + h.SingleLine = objectString(obj, i.qf, nil) + } + obj := i.Declaration.obj + if obj == nil { + return h, nil + } + + // Check if the identifier is test-only (and is therefore not part of a + // package's API). This is true if the request originated in a test package, + // and if the declaration is also found in the same test package. + if i.pkg != nil && obj.Pkg() != nil && i.pkg.ForTest() != "" { + if _, err := i.pkg.File(i.Declaration.MappedRange[0].URI()); err == nil { + return h, nil + } + } + + h.SymbolName, h.LinkPath, h.LinkAnchor = linkData(obj, i.enclosing) + + // See golang/go#36998: don't link to modules matching GOPRIVATE. + // + // The path returned by linkData is a package path. + if i.Snapshot.View().IsGoPrivatePath(h.LinkPath) { + h.LinkPath = "" + } else if mod, version, ok := moduleAtVersion(h.LinkPath, i); ok { + h.LinkPath = strings.Replace(h.LinkPath, mod, mod+"@"+version, 1) + } + + return h, nil +} + +// linkData returns the name, package path, and anchor to use in building links +// to obj. +// +// If obj is not visible in documentation, the returned name will be empty. +func linkData(obj types.Object, enclosing *types.TypeName) (name, packagePath, anchor string) { + // Package names simply link to the package. + if obj, ok := obj.(*types.PkgName); ok { + return obj.Name(), obj.Imported().Path(), "" + } + + // Builtins link to the special builtin package. + if obj.Parent() == types.Universe { + return obj.Name(), "builtin", obj.Name() + } + + // In all other cases, the object must be exported. + if !obj.Exported() { + return "", "", "" + } + + var recv types.Object // If non-nil, the field or method receiver base. + + switch obj := obj.(type) { + case *types.Var: + // If the object is a field, and we have an associated selector + // composite literal, or struct, we can determine the link. + if obj.IsField() && enclosing != nil { + recv = enclosing + } + case *types.Func: + typ, ok := obj.Type().(*types.Signature) + if !ok { + // Note: this should never happen. go/types guarantees that the type of + // *Funcs are Signatures. + // + // TODO(rfindley): given a 'debug' mode, we should panic here. + return "", "", "" + } + if r := typ.Recv(); r != nil { + if rtyp, _ := Deref(r.Type()).(*types.Named); rtyp != nil { + // If we have an unexported type, see if the enclosing type is + // exported (we may have an interface or struct we can link + // to). If not, don't show any link. + if !rtyp.Obj().Exported() { + if enclosing != nil { + recv = enclosing + } else { + return "", "", "" + } + } else { + recv = rtyp.Obj() + } + } + } + } + + if recv != nil && !recv.Exported() { + return "", "", "" + } + + // Either the object or its receiver must be in the package scope. + scopeObj := obj + if recv != nil { + scopeObj = recv + } + if scopeObj.Pkg() == nil || scopeObj.Pkg().Scope().Lookup(scopeObj.Name()) != scopeObj { + return "", "", "" + } + + // golang/go#52211: somehow we get here with a nil obj.Pkg + if obj.Pkg() == nil { + bug.Report("object with nil pkg", bug.Data{ + "name": obj.Name(), + "type": fmt.Sprintf("%T", obj), + }) + return "", "", "" + } + + packagePath = obj.Pkg().Path() + if recv != nil { + anchor = fmt.Sprintf("%s.%s", recv.Name(), obj.Name()) + name = fmt.Sprintf("(%s.%s).%s", obj.Pkg().Name(), recv.Name(), obj.Name()) + } else { + // For most cases, the link is "package/path#symbol". + anchor = obj.Name() + name = fmt.Sprintf("%s.%s", obj.Pkg().Name(), obj.Name()) + } + return name, packagePath, anchor +} + +func moduleAtVersion(path string, i *IdentifierInfo) (string, string, bool) { + // TODO(rfindley): moduleAtVersion should not be responsible for deciding + // whether or not the link target supports module version links. + if strings.ToLower(i.Snapshot.View().Options().LinkTarget) != "pkg.go.dev" { + return "", "", false + } + impPkg, err := i.pkg.DirectDep(PackagePath(path)) + if err != nil { + return "", "", false + } + if impPkg.Version() == nil { + return "", "", false + } + version, modpath := impPkg.Version().Version, impPkg.Version().Path + if modpath == "" || version == "" { + return "", "", false + } + return modpath, version, true +} + +// objectString is a wrapper around the types.ObjectString function. +// It handles adding more information to the object string. +func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string { + // If the signature type was inferred, prefer the preferred signature with a + // comment showing the generic signature. + if sig, _ := obj.Type().(*types.Signature); sig != nil && typeparams.ForSignature(sig).Len() > 0 && inferred != nil { + obj2 := types.NewFunc(obj.Pos(), obj.Pkg(), obj.Name(), inferred) + str := types.ObjectString(obj2, qf) + // Try to avoid overly long lines. + if len(str) > 60 { + str += "\n" + } else { + str += " " + } + str += "// " + types.TypeString(sig, qf) + return str + } + str := types.ObjectString(obj, qf) + switch obj := obj.(type) { + case *types.Const: + str = fmt.Sprintf("%s = %s", str, obj.Val()) + + // Try to add a formatted duration as an inline comment + typ, ok := obj.Type().(*types.Named) + if !ok { + break + } + pkg := typ.Obj().Pkg() + if pkg.Path() == "time" && typ.Obj().Name() == "Duration" { + if d, ok := constant.Int64Val(obj.Val()); ok { + str += " // " + time.Duration(d).String() + } + } + } + return str +} + +// FindHoverContext returns a HoverContext struct for an AST node and its +// declaration object. node should be the actual node used in type checking, +// while fullNode could be a separate node with more complete syntactic +// information. +func FindHoverContext(ctx context.Context, s Snapshot, pkg Package, obj types.Object, pkgNode ast.Node, fullDecl ast.Decl) (*HoverContext, error) { + var info *HoverContext + + // Type parameters get their signature from their declaration object. + if _, isTypeName := obj.(*types.TypeName); isTypeName { + if _, isTypeParam := obj.Type().(*typeparams.TypeParam); isTypeParam { + return &HoverContext{signatureSource: obj}, nil + } + } + + // This is problematic for a number of reasons. We really need to have a more + // general mechanism to validate the coherency of AST with type information, + // but absent that we must do our best to ensure that we don't use fullNode + // when we actually need the node that was type checked. + // + // pkgNode may be nil, if it was eliminated from the type-checked syntax. In + // that case, use fullDecl if available. + node := pkgNode + if node == nil && fullDecl != nil { + node = fullDecl + } + + switch node := node.(type) { + case *ast.Ident: + // The package declaration. + for _, f := range pkg.GetSyntax() { + if f.Name == pkgNode { + info = &HoverContext{Comment: f.Doc} + } + } + case *ast.ImportSpec: + // Try to find the package documentation for an imported package. + importPath, err := strconv.Unquote(node.Path.Value) + if err != nil { + return nil, err + } + imp, err := pkg.ResolveImportPath(ImportPath(importPath)) + if err != nil { + return nil, err + } + // Assume that only one file will contain package documentation, + // so pick the first file that has a doc comment. + for _, file := range imp.GetSyntax() { + if file.Doc != nil { + info = &HoverContext{Comment: file.Doc} + if file.Name != nil { + info.signatureSource = "package " + file.Name.Name + } + break + } + } + case *ast.GenDecl: + switch obj := obj.(type) { + case *types.TypeName, *types.Var, *types.Const, *types.Func: + // Always use the full declaration here if we have it, because the + // dependent code doesn't rely on pointer identity. This is fragile. + if d, _ := fullDecl.(*ast.GenDecl); d != nil { + node = d + } + // obj may not have been produced by type checking the AST containing + // node, so we need to be careful about using token.Pos. + tok := pkg.FileSet().File(obj.Pos()) + offset, err := safetoken.Offset(tok, obj.Pos()) + if err != nil { + return nil, err + } + + // fullTok and fullPos are the *token.File and object position in for the + // full AST. + fullTok := pkg.FileSet().File(node.Pos()) + fullPos, err := safetoken.Pos(fullTok, offset) + if err != nil { + return nil, err + } + + var spec ast.Spec + for _, s := range node.Specs { + // Avoid panics by guarding the calls to token.Offset (golang/go#48249). + start, err := safetoken.Offset(fullTok, s.Pos()) + if err != nil { + return nil, err + } + end, err := safetoken.Offset(fullTok, s.End()) + if err != nil { + return nil, err + } + if start <= offset && offset <= end { + spec = s + break + } + } + + info, err = hoverGenDecl(node, spec, fullPos, obj) + if err != nil { + return nil, err + } + } + case *ast.TypeSpec: + if obj.Parent() == types.Universe { + if genDecl, ok := fullDecl.(*ast.GenDecl); ok { + info = hoverTypeSpec(node, genDecl) + } + } + case *ast.FuncDecl: + switch obj.(type) { + case *types.Func: + info = &HoverContext{signatureSource: obj, Comment: node.Doc} + case *types.Builtin: + info = &HoverContext{Comment: node.Doc} + if sig, err := NewBuiltinSignature(ctx, s, obj.Name()); err == nil { + info.signatureSource = "func " + sig.name + sig.Format() + } else { + // Fall back on the object as a signature source. + bug.Report("invalid builtin hover", bug.Data{ + "err": err.Error(), + }) + info.signatureSource = obj + } + case *types.Var: + // Object is a function param or the field of an anonymous struct + // declared with ':='. Skip the first one because only fields + // can have docs. + if isFunctionParam(obj, node) { + break + } + + _, field := FindDeclAndField(pkg.GetSyntax(), obj.Pos()) + if field != nil { + comment := field.Doc + if comment.Text() == "" { + comment = field.Comment + } + info = &HoverContext{signatureSource: obj, Comment: comment} + } + } + } + + if info == nil { + info = &HoverContext{signatureSource: obj} + } + + return info, nil +} + +// isFunctionParam returns true if the passed object is either an incoming +// or an outgoing function param +func isFunctionParam(obj types.Object, node *ast.FuncDecl) bool { + for _, f := range node.Type.Params.List { + if f.Pos() == obj.Pos() { + return true + } + } + if node.Type.Results != nil { + for _, f := range node.Type.Results.List { + if f.Pos() == obj.Pos() { + return true + } + } + } + return false +} + +// hoverGenDecl returns hover information an object declared via spec inside +// of the GenDecl node. obj is the type-checked object corresponding to the +// declaration, but may have been type-checked using a different AST than the +// given nodes; fullPos is the position of obj in node's AST. +func hoverGenDecl(node *ast.GenDecl, spec ast.Spec, fullPos token.Pos, obj types.Object) (*HoverContext, error) { + if spec == nil { + return nil, fmt.Errorf("no spec for node %v at position %v", node, fullPos) + } + + // If we have a field or method. + switch obj.(type) { + case *types.Var, *types.Const, *types.Func: + return hoverVar(spec, fullPos, obj, node), nil + } + // Handle types. + switch spec := spec.(type) { + case *ast.TypeSpec: + return hoverTypeSpec(spec, node), nil + case *ast.ValueSpec: + return &HoverContext{signatureSource: spec, Comment: spec.Doc}, nil + case *ast.ImportSpec: + return &HoverContext{signatureSource: spec, Comment: spec.Doc}, nil + } + return nil, fmt.Errorf("unable to format spec %v (%T)", spec, spec) +} + +// TODO(rfindley): rename this function. +func hoverTypeSpec(spec *ast.TypeSpec, decl *ast.GenDecl) *HoverContext { + comment := spec.Doc + if comment == nil && decl != nil { + comment = decl.Doc + } + if comment == nil { + comment = spec.Comment + } + return &HoverContext{ + signatureSource: spec, + Comment: comment, + } +} + +func hoverVar(node ast.Spec, fullPos token.Pos, obj types.Object, decl *ast.GenDecl) *HoverContext { + var fieldList *ast.FieldList + switch spec := node.(type) { + case *ast.TypeSpec: + switch t := spec.Type.(type) { + case *ast.StructType: + fieldList = t.Fields + case *ast.InterfaceType: + fieldList = t.Methods + } + case *ast.ValueSpec: + // Try to extract the field list of an anonymous struct + if fieldList = extractFieldList(spec.Type); fieldList != nil { + break + } + + comment := spec.Doc + if comment == nil { + comment = decl.Doc + } + if comment == nil { + comment = spec.Comment + } + + // We need the AST nodes for variable declarations of basic literals with + // associated values so that we can augment their hover with more information. + if _, ok := obj.(*types.Var); ok && spec.Type == nil && len(spec.Values) > 0 { + if _, ok := spec.Values[0].(*ast.BasicLit); ok { + return &HoverContext{signatureSource: spec, Comment: comment} + } + } + + return &HoverContext{signatureSource: obj, Comment: comment} + } + + if fieldList != nil { + comment := findFieldComment(fullPos, fieldList) + return &HoverContext{signatureSource: obj, Comment: comment} + } + return &HoverContext{signatureSource: obj, Comment: decl.Doc} +} + +// extractFieldList recursively tries to extract a field list. +// If it is not found, nil is returned. +func extractFieldList(specType ast.Expr) *ast.FieldList { + switch t := specType.(type) { + case *ast.StructType: + return t.Fields + case *ast.InterfaceType: + return t.Methods + case *ast.ArrayType: + return extractFieldList(t.Elt) + case *ast.MapType: + // Map value has a greater chance to be a struct + if fields := extractFieldList(t.Value); fields != nil { + return fields + } + return extractFieldList(t.Key) + case *ast.ChanType: + return extractFieldList(t.Value) + } + return nil +} + +// findFieldComment visits all fields in depth-first order and returns +// the comment of a field with passed position. If no comment is found, +// nil is returned. +func findFieldComment(pos token.Pos, fieldList *ast.FieldList) *ast.CommentGroup { + for _, field := range fieldList.List { + if field.Pos() == pos { + if field.Doc.Text() != "" { + return field.Doc + } + return field.Comment + } + + if nestedFieldList := extractFieldList(field.Type); nestedFieldList != nil { + if c := findFieldComment(pos, nestedFieldList); c != nil { + return c + } + } + } + return nil +} + +func FormatHover(h *HoverJSON, options *Options) (string, error) { + signature := formatSignature(h, options) + + switch options.HoverKind { + case SingleLine: + return h.SingleLine, nil + case NoDocumentation: + return signature, nil + case Structured: + b, err := json.Marshal(h) + if err != nil { + return "", err + } + return string(b), nil + } + + link := formatLink(h, options) + doc := formatDoc(h, options) + + var b strings.Builder + parts := []string{signature, doc, link} + for i, el := range parts { + if el != "" { + b.WriteString(el) + + // If any elements of the remainder of the list are non-empty, + // write an extra newline. + if anyNonEmpty(parts[i+1:]) { + if options.PreferredContentFormat == protocol.Markdown { + b.WriteString("\n\n") + } else { + b.WriteRune('\n') + } + } + } + } + return b.String(), nil +} + +func formatSignature(h *HoverJSON, options *Options) string { + signature := h.Signature + if signature != "" && options.PreferredContentFormat == protocol.Markdown { + signature = fmt.Sprintf("```go\n%s\n```", signature) + } + return signature +} + +func formatLink(h *HoverJSON, options *Options) string { + if !options.LinksInHover || options.LinkTarget == "" || h.LinkPath == "" { + return "" + } + plainLink := BuildLink(options.LinkTarget, h.LinkPath, h.LinkAnchor) + switch options.PreferredContentFormat { + case protocol.Markdown: + return fmt.Sprintf("[`%s` on %s](%s)", h.SymbolName, options.LinkTarget, plainLink) + case protocol.PlainText: + return "" + default: + return plainLink + } +} + +// BuildLink constructs a link with the given target, path, and anchor. +func BuildLink(target, path, anchor string) string { + link := fmt.Sprintf("https://%s/%s", target, path) + if anchor == "" { + return link + } + return link + "#" + anchor +} + +func formatDoc(h *HoverJSON, options *Options) string { + var doc string + switch options.HoverKind { + case SynopsisDocumentation: + doc = h.Synopsis + case FullDocumentation: + doc = h.FullDocumentation + } + if options.PreferredContentFormat == protocol.Markdown { + return CommentToMarkdown(doc) + } + return doc +} + +func anyNonEmpty(x []string) bool { + for _, el := range x { + if el != "" { + return true + } + } + return false +} + +// FindDeclAndField returns the var/func/type/const Decl that declares +// the identifier at pos, searching the given list of file syntax +// trees. If pos is the position of an ast.Field or one of its Names +// or Ellipsis.Elt, the field is returned, along with the innermost +// enclosing Decl, which could be only loosely related---consider: +// +// var decl = f( func(field int) {} ) +// +// It returns (nil, nil) if no Field or Decl is found at pos. +func FindDeclAndField(files []*ast.File, pos token.Pos) (decl ast.Decl, field *ast.Field) { + // panic(nil) breaks off the traversal and + // causes the function to return normally. + defer func() { + if x := recover(); x != nil { + panic(x) + } + }() + + // Visit the files in search of the node at pos. + stack := make([]ast.Node, 0, 20) + // Allocate the closure once, outside the loop. + f := func(n ast.Node) bool { + if n != nil { + stack = append(stack, n) // push + } else { + stack = stack[:len(stack)-1] // pop + return false + } + + // Skip subtrees (incl. files) that don't contain the search point. + if !(n.Pos() <= pos && pos < n.End()) { + return false + } + + switch n := n.(type) { + case *ast.Field: + checkField := func(f ast.Node) { + if f.Pos() == pos { + field = n + for i := len(stack) - 1; i >= 0; i-- { + if d, ok := stack[i].(ast.Decl); ok { + decl = d // innermost enclosing decl + break + } + } + panic(nil) // found + } + } + + // Check *ast.Field itself. This handles embedded + // fields which have no associated *ast.Ident name. + checkField(n) + + // Check each field name since you can have + // multiple names for the same type expression. + for _, name := range n.Names { + checkField(name) + } + + // Also check "X" in "...X". This makes it easy + // to format variadic signature params properly. + if ell, ok := n.Type.(*ast.Ellipsis); ok && ell.Elt != nil { + checkField(ell.Elt) + } + + case *ast.FuncDecl: + if n.Name.Pos() == pos { + decl = n + panic(nil) // found + } + + case *ast.GenDecl: + for _, spec := range n.Specs { + switch spec := spec.(type) { + case *ast.TypeSpec: + if spec.Name.Pos() == pos { + decl = n + panic(nil) // found + } + case *ast.ValueSpec: + for _, id := range spec.Names { + if id.Pos() == pos { + decl = n + panic(nil) // found + } + } + } + } + } + return true + } + for _, file := range files { + ast.Inspect(file, f) + } + + return nil, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/identifier.go b/gopls/golang_org_x_tools_gopls/lsp/source/identifier.go new file mode 100644 index 0000000..cb4b379 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/identifier.go @@ -0,0 +1,556 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "go/types" + "strconv" + + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/bug" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +// IdentifierInfo holds information about an identifier in Go source. +type IdentifierInfo struct { + Name string + Snapshot Snapshot // only needed for .View(); TODO(adonovan): reduce. + MappedRange + + Type struct { + MappedRange + Object types.Object + } + + Inferred *types.Signature + + Declaration Declaration + + ident *ast.Ident + + // For struct fields or embedded interfaces, enclosing is the object + // corresponding to the outer type declaration, if it is exported, for use in + // documentation links. + enclosing *types.TypeName + + pkg Package + qf types.Qualifier +} + +func (i *IdentifierInfo) IsImport() bool { + _, ok := i.Declaration.node.(*ast.ImportSpec) + return ok +} + +type Declaration struct { + MappedRange []MappedRange + + // The typechecked node. + node ast.Node + + // Optional: the fully parsed node, to be used for formatting in cases where + // node has missing information. This could be the case when node was parsed + // in ParseExported mode. + fullDecl ast.Decl + + // The typechecked object. + obj types.Object + + // typeSwitchImplicit indicates that the declaration is in an implicit + // type switch. Its type is the type of the variable on the right-hand + // side of the type switch. + typeSwitchImplicit types.Type +} + +// Identifier returns identifier information for a position +// in a file, accounting for a potentially incomplete selector. +func Identifier(ctx context.Context, snapshot Snapshot, fh FileHandle, position protocol.Position) (*IdentifierInfo, error) { + ctx, done := event.Start(ctx, "source.Identifier") + defer done() + + pkg, err := snapshot.PackageForFile(ctx, fh.URI(), TypecheckFull, NarrowestPackage) + if err != nil { + return nil, err + } + pgf, err := pkg.File(fh.URI()) + if err != nil { + // We shouldn't get a package from PackagesForFile that doesn't actually + // contain the file. + bug.Report("missing package file", bug.Data{"pkg": pkg.ID(), "file": fh.URI()}) + return nil, err + } + pos, err := pgf.Mapper.Pos(position) + if err != nil { + return nil, err + } + return findIdentifier(ctx, snapshot, pkg, pgf, pos) +} + +// ErrNoIdentFound is error returned when no identifier is found at a particular position +var ErrNoIdentFound = errors.New("no identifier found") + +func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, pgf *ParsedGoFile, pos token.Pos) (*IdentifierInfo, error) { + file := pgf.File + // Handle import specs separately, as there is no formal position for a + // package declaration. + if result, err := importSpec(snapshot, pkg, file, pos); result != nil || err != nil { + return result, err + } + path := pathEnclosingObjNode(file, pos) + if path == nil { + return nil, ErrNoIdentFound + } + + qf := Qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()) + + ident, _ := path[0].(*ast.Ident) + if ident == nil { + return nil, ErrNoIdentFound + } + // Special case for package declarations, since they have no + // corresponding types.Object. + if ident == file.Name { + rng, err := posToMappedRange(pkg, file.Name.Pos(), file.Name.End()) + if err != nil { + return nil, err + } + var declAST *ast.File + for _, pgf := range pkg.CompiledGoFiles() { + if pgf.File.Doc != nil { + declAST = pgf.File + } + } + // If there's no package documentation, just use current file. + if declAST == nil { + declAST = file + } + declRng, err := posToMappedRange(pkg, declAST.Name.Pos(), declAST.Name.End()) + if err != nil { + return nil, err + } + return &IdentifierInfo{ + Name: file.Name.Name, + ident: file.Name, + MappedRange: rng, + pkg: pkg, + qf: qf, + Snapshot: snapshot, + Declaration: Declaration{ + node: declAST.Name, + MappedRange: []MappedRange{declRng}, + }, + }, nil + } + + result := &IdentifierInfo{ + Snapshot: snapshot, + qf: qf, + pkg: pkg, + ident: ident, + enclosing: searchForEnclosing(pkg.GetTypesInfo(), path), + } + + result.Name = result.ident.Name + var err error + if result.MappedRange, err = posToMappedRange(pkg, result.ident.Pos(), result.ident.End()); err != nil { + return nil, err + } + + result.Declaration.obj = pkg.GetTypesInfo().ObjectOf(result.ident) + if result.Declaration.obj == nil { + // If there was no types.Object for the declaration, there might be an + // implicit local variable declaration in a type switch. + if objs, typ := typeSwitchImplicits(pkg, path); len(objs) > 0 { + // There is no types.Object for the declaration of an implicit local variable, + // but all of the types.Objects associated with the usages of this variable can be + // used to connect it back to the declaration. + // Preserve the first of these objects and treat it as if it were the declaring object. + result.Declaration.obj = objs[0] + result.Declaration.typeSwitchImplicit = typ + } else { + // Probably a type error. + return nil, fmt.Errorf("%w for ident %v", errNoObjectFound, result.Name) + } + } + + // Handle builtins separately. + if result.Declaration.obj.Parent() == types.Universe { + builtin, err := snapshot.BuiltinFile(ctx) + if err != nil { + return nil, err + } + builtinObj := builtin.File.Scope.Lookup(result.Name) + if builtinObj == nil { + return nil, fmt.Errorf("no builtin object for %s", result.Name) + } + decl, ok := builtinObj.Decl.(ast.Node) + if !ok { + return nil, fmt.Errorf("no declaration for %s", result.Name) + } + result.Declaration.node = decl + if typeSpec, ok := decl.(*ast.TypeSpec); ok { + // Find the GenDecl (which has the doc comments) for the TypeSpec. + result.Declaration.fullDecl = findGenDecl(builtin.File, typeSpec) + } + + // The builtin package isn't in the dependency graph, so the usual + // utilities won't work here. + rng := NewMappedRange(builtin.Tok, builtin.Mapper, decl.Pos(), decl.Pos()+token.Pos(len(result.Name))) + result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng) + return result, nil + } + + // (error).Error is a special case of builtin. Lots of checks to confirm + // that this is the builtin Error. + if obj := result.Declaration.obj; obj.Parent() == nil && obj.Pkg() == nil && obj.Name() == "Error" { + if _, ok := obj.Type().(*types.Signature); ok { + builtin, err := snapshot.BuiltinFile(ctx) + if err != nil { + return nil, err + } + // Look up "error" and then navigate to its only method. + // The Error method does not appear in the builtin package's scope.log.Pri + const errorName = "error" + builtinObj := builtin.File.Scope.Lookup(errorName) + if builtinObj == nil { + return nil, fmt.Errorf("no builtin object for %s", errorName) + } + decl, ok := builtinObj.Decl.(ast.Node) + if !ok { + return nil, fmt.Errorf("no declaration for %s", errorName) + } + spec, ok := decl.(*ast.TypeSpec) + if !ok { + return nil, fmt.Errorf("no type spec for %s", errorName) + } + iface, ok := spec.Type.(*ast.InterfaceType) + if !ok { + return nil, fmt.Errorf("%s is not an interface", errorName) + } + if iface.Methods.NumFields() != 1 { + return nil, fmt.Errorf("expected 1 method for %s, got %v", errorName, iface.Methods.NumFields()) + } + method := iface.Methods.List[0] + if len(method.Names) != 1 { + return nil, fmt.Errorf("expected 1 name for %v, got %v", method, len(method.Names)) + } + name := method.Names[0].Name + result.Declaration.node = method + rng := NewMappedRange(builtin.Tok, builtin.Mapper, method.Pos(), method.Pos()+token.Pos(len(name))) + result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng) + return result, nil + } + } + + // If the original position was an embedded field, we want to jump + // to the field's type definition, not the field's definition. + if v, ok := result.Declaration.obj.(*types.Var); ok && v.Embedded() { + // types.Info.Uses contains the embedded field's *types.TypeName. + if typeName := pkg.GetTypesInfo().Uses[ident]; typeName != nil { + result.Declaration.obj = typeName + } + } + + rng, err := objToMappedRange(pkg, result.Declaration.obj) + if err != nil { + return nil, err + } + result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng) + + declPkg, err := FindPackageFromPos(pkg, result.Declaration.obj.Pos()) + if err != nil { + return nil, err + } + result.Declaration.node, _ = FindDeclAndField(declPkg.GetSyntax(), result.Declaration.obj.Pos()) // may be nil + + // Ensure that we have the full declaration, in case the declaration was + // parsed in ParseExported and therefore could be missing information. + if result.Declaration.fullDecl, err = fullNode(pkg.FileSet(), result.Declaration.obj, declPkg); err != nil { + return nil, err + } + typ := pkg.GetTypesInfo().TypeOf(result.ident) + if typ == nil { + return result, nil + } + + result.Inferred = inferredSignature(pkg.GetTypesInfo(), ident) + + result.Type.Object = typeToObject(typ) + if result.Type.Object != nil { + // Identifiers with the type "error" are a special case with no position. + if hasErrorType(result.Type.Object) { + return result, nil + } + if result.Type.MappedRange, err = objToMappedRange(pkg, result.Type.Object); err != nil { + return nil, err + } + } + return result, nil +} + +// findGenDecl determines the parent ast.GenDecl for a given ast.Spec. +func findGenDecl(f *ast.File, spec ast.Spec) *ast.GenDecl { + for _, decl := range f.Decls { + if genDecl, ok := decl.(*ast.GenDecl); ok { + if genDecl.Pos() <= spec.Pos() && genDecl.End() >= spec.End() { + return genDecl + } + } + } + return nil +} + +// fullNode tries to extract the full spec corresponding to obj's declaration. +// If the package was not parsed in full, the declaration file will be +// re-parsed to ensure it has complete syntax. +func fullNode(fset *token.FileSet, obj types.Object, pkg Package) (ast.Decl, error) { + // declaration in a different package... make sure we have full AST information. + tok := fset.File(obj.Pos()) + uri := span.URIFromPath(tok.Name()) + pgf, err := pkg.File(uri) + if err != nil { + return nil, err + } + file := pgf.File + pos := obj.Pos() + if pgf.Mode != ParseFull { + file2, _ := parser.ParseFile(fset, tok.Name(), pgf.Src, parser.AllErrors|parser.ParseComments) + if file2 != nil { + offset, err := safetoken.Offset(tok, obj.Pos()) + if err != nil { + return nil, err + } + file = file2 + tok2 := fset.File(file2.Pos()) + pos = tok2.Pos(offset) + } + } + path, _ := astutil.PathEnclosingInterval(file, pos, pos) + for _, n := range path { + if decl, ok := n.(ast.Decl); ok { + return decl, nil + } + } + return nil, nil +} + +// inferredSignature determines the resolved non-generic signature for an +// identifier in an instantiation expression. +// +// If no such signature exists, it returns nil. +func inferredSignature(info *types.Info, id *ast.Ident) *types.Signature { + inst := typeparams.GetInstances(info)[id] + sig, _ := inst.Type.(*types.Signature) + return sig +} + +func searchForEnclosing(info *types.Info, path []ast.Node) *types.TypeName { + for _, n := range path { + switch n := n.(type) { + case *ast.SelectorExpr: + if sel, ok := info.Selections[n]; ok { + recv := Deref(sel.Recv()) + + // Keep track of the last exported type seen. + var exported *types.TypeName + if named, ok := recv.(*types.Named); ok && named.Obj().Exported() { + exported = named.Obj() + } + // We don't want the last element, as that's the field or + // method itself. + for _, index := range sel.Index()[:len(sel.Index())-1] { + if r, ok := recv.Underlying().(*types.Struct); ok { + recv = Deref(r.Field(index).Type()) + if named, ok := recv.(*types.Named); ok && named.Obj().Exported() { + exported = named.Obj() + } + } + } + return exported + } + case *ast.CompositeLit: + if t, ok := info.Types[n]; ok { + if named, _ := t.Type.(*types.Named); named != nil { + return named.Obj() + } + } + case *ast.TypeSpec: + if _, ok := n.Type.(*ast.StructType); ok { + if t, ok := info.Defs[n.Name]; ok { + if tname, _ := t.(*types.TypeName); tname != nil { + return tname + } + } + } + } + } + return nil +} + +func typeToObject(typ types.Type) types.Object { + switch typ := typ.(type) { + case *types.Named: + return typ.Obj() + case *types.Pointer: + return typeToObject(typ.Elem()) + case *types.Array: + return typeToObject(typ.Elem()) + case *types.Slice: + return typeToObject(typ.Elem()) + case *types.Chan: + return typeToObject(typ.Elem()) + case *types.Signature: + // Try to find a return value of a named type. If there's only one + // such value, jump to its type definition. + var res types.Object + + results := typ.Results() + for i := 0; i < results.Len(); i++ { + obj := typeToObject(results.At(i).Type()) + if obj == nil || hasErrorType(obj) { + // Skip builtins. + continue + } + if res != nil { + // The function/method must have only one return value of a named type. + return nil + } + + res = obj + } + return res + default: + return nil + } +} + +func hasErrorType(obj types.Object) bool { + return types.IsInterface(obj.Type()) && obj.Pkg() == nil && obj.Name() == "error" +} + +// importSpec handles positions inside of an *ast.ImportSpec. +func importSpec(snapshot Snapshot, pkg Package, file *ast.File, pos token.Pos) (*IdentifierInfo, error) { + var imp *ast.ImportSpec + for _, spec := range file.Imports { + if spec.Path.Pos() <= pos && pos < spec.Path.End() { + imp = spec + } + } + if imp == nil { + return nil, nil + } + importPath, err := strconv.Unquote(imp.Path.Value) + if err != nil { + return nil, fmt.Errorf("import path not quoted: %s (%v)", imp.Path.Value, err) + } + imported, err := pkg.ResolveImportPath(ImportPath(importPath)) + if err != nil { + return nil, err + } + result := &IdentifierInfo{ + Snapshot: snapshot, + Name: importPath, // should this perhaps be imported.PkgPath()? + pkg: pkg, + } + if result.MappedRange, err = posToMappedRange(pkg, imp.Path.Pos(), imp.Path.End()); err != nil { + return nil, err + } + // Consider the "declaration" of an import spec to be the imported package. + // Return all of the files in the package as the definition of the import spec. + for _, dst := range imported.GetSyntax() { + rng, err := posToMappedRange(pkg, dst.Pos(), dst.End()) + if err != nil { + return nil, err + } + result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng) + } + + result.Declaration.node = imp + return result, nil +} + +// typeSwitchImplicits returns all the implicit type switch objects that +// correspond to the leaf *ast.Ident. It also returns the original type +// associated with the identifier (outside of a case clause). +func typeSwitchImplicits(pkg Package, path []ast.Node) ([]types.Object, types.Type) { + ident, _ := path[0].(*ast.Ident) + if ident == nil { + return nil, nil + } + + var ( + ts *ast.TypeSwitchStmt + assign *ast.AssignStmt + cc *ast.CaseClause + obj = pkg.GetTypesInfo().ObjectOf(ident) + ) + + // Walk our ancestors to determine if our leaf ident refers to a + // type switch variable, e.g. the "a" from "switch a := b.(type)". +Outer: + for i := 1; i < len(path); i++ { + switch n := path[i].(type) { + case *ast.AssignStmt: + // Check if ident is the "a" in "a := foo.(type)". The "a" in + // this case has no types.Object, so check for ident equality. + if len(n.Lhs) == 1 && n.Lhs[0] == ident { + assign = n + } + case *ast.CaseClause: + // Check if ident is a use of "a" within a case clause. Each + // case clause implicitly maps "a" to a different types.Object, + // so check if ident's object is the case clause's implicit + // object. + if obj != nil && pkg.GetTypesInfo().Implicits[n] == obj { + cc = n + } + case *ast.TypeSwitchStmt: + // Look for the type switch that owns our previously found + // *ast.AssignStmt or *ast.CaseClause. + if n.Assign == assign { + ts = n + break Outer + } + + for _, stmt := range n.Body.List { + if stmt == cc { + ts = n + break Outer + } + } + } + } + if ts == nil { + return nil, nil + } + // Our leaf ident refers to a type switch variable. Fan out to the + // type switch's implicit case clause objects. + var objs []types.Object + for _, cc := range ts.Body.List { + if ccObj := pkg.GetTypesInfo().Implicits[cc]; ccObj != nil { + objs = append(objs, ccObj) + } + } + // The right-hand side of a type switch should only have one + // element, and we need to track its type in order to generate + // hover information for implicit type switch variables. + var typ types.Type + if assign, ok := ts.Assign.(*ast.AssignStmt); ok && len(assign.Rhs) == 1 { + if rhs := assign.Rhs[0].(*ast.TypeAssertExpr); ok { + typ = pkg.GetTypesInfo().TypeOf(rhs.X) + } + } + return objs, typ +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/implementation.go b/gopls/golang_org_x_tools_gopls/lsp/source/implementation.go new file mode 100644 index 0000000..78a9b9c --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/implementation.go @@ -0,0 +1,477 @@ +// 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 source + +import ( + "context" + "errors" + "fmt" + "go/ast" + "go/token" + "go/types" + "sort" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +func Implementation(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position) ([]protocol.Location, error) { + ctx, done := event.Start(ctx, "source.Implementation") + defer done() + + impls, err := implementations(ctx, snapshot, f, pp) + if err != nil { + return nil, err + } + var locations []protocol.Location + for _, impl := range impls { + if impl.pkg == nil || len(impl.pkg.CompiledGoFiles()) == 0 { + continue + } + rng, err := objToMappedRange(impl.pkg, impl.obj) + if err != nil { + return nil, err + } + pr, err := rng.Range() + if err != nil { + return nil, err + } + locations = append(locations, protocol.Location{ + URI: protocol.URIFromSpanURI(rng.URI()), + Range: pr, + }) + } + sort.Slice(locations, func(i, j int) bool { + li, lj := locations[i], locations[j] + if li.URI == lj.URI { + return protocol.CompareRange(li.Range, lj.Range) < 0 + } + return li.URI < lj.URI + }) + return locations, nil +} + +var ErrNotAType = errors.New("not a type name or method") + +// implementations returns the concrete implementations of the specified +// interface, or the interfaces implemented by the specified concrete type. +// It populates only the definition-related fields of qualifiedObject. +// (Arguably it should return a smaller data type.) +func implementations(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position) ([]qualifiedObject, error) { + // Find all named types, even local types + // (which can have methods due to promotion). + var ( + allNamed []*types.Named + pkgs = make(map[*types.Package]Package) + ) + knownPkgs, err := s.KnownPackages(ctx) + if err != nil { + return nil, err + } + for _, pkg := range knownPkgs { + pkgs[pkg.GetTypes()] = pkg + for _, obj := range pkg.GetTypesInfo().Defs { + obj, ok := obj.(*types.TypeName) + // We ignore aliases 'type M = N' to avoid duplicate reporting + // of the Named type N. + if !ok || obj.IsAlias() { + continue + } + if named, ok := obj.Type().(*types.Named); ok { + allNamed = append(allNamed, named) + } + } + } + + qos, err := qualifiedObjsAtProtocolPos(ctx, s, f.URI(), pp) + if err != nil { + return nil, err + } + var ( + impls []qualifiedObject + seen = make(map[token.Position]bool) + ) + for _, qo := range qos { + // Ascertain the query identifier (type or method). + var ( + queryType types.Type + queryMethod *types.Func + ) + switch obj := qo.obj.(type) { + case *types.Func: + queryMethod = obj + if recv := obj.Type().(*types.Signature).Recv(); recv != nil { + queryType = ensurePointer(recv.Type()) + } + case *types.TypeName: + queryType = ensurePointer(obj.Type()) + } + + if queryType == nil { + return nil, ErrNotAType + } + + if types.NewMethodSet(queryType).Len() == 0 { + return nil, nil + } + + // Find all the named types that match our query. + for _, named := range allNamed { + var ( + candObj types.Object = named.Obj() + candType = ensurePointer(named) + ) + + if !concreteImplementsIntf(candType, queryType) { + continue + } + + ms := types.NewMethodSet(candType) + if ms.Len() == 0 { + // Skip empty interfaces. + continue + } + + // If client queried a method, look up corresponding candType method. + if queryMethod != nil { + sel := ms.Lookup(queryMethod.Pkg(), queryMethod.Name()) + if sel == nil { + continue + } + candObj = sel.Obj() + } + + if candObj == queryMethod { + continue + } + + pkg := pkgs[candObj.Pkg()] // may be nil (e.g. error) + + // TODO(adonovan): the logic below assumes there is only one + // predeclared (pkg=nil) object of interest, the error type. + // That could change in a future version of Go. + + var posn token.Position + if pkg != nil { + posn = pkg.FileSet().Position(candObj.Pos()) + } + if seen[posn] { + continue + } + seen[posn] = true + + impls = append(impls, qualifiedObject{ + obj: candObj, + pkg: pkg, + }) + } + } + + return impls, nil +} + +// concreteImplementsIntf returns true if a is an interface type implemented by +// concrete type b, or vice versa. +func concreteImplementsIntf(a, b types.Type) bool { + aIsIntf, bIsIntf := IsInterface(a), IsInterface(b) + + // Make sure exactly one is an interface type. + if aIsIntf == bIsIntf { + return false + } + + // Rearrange if needed so "a" is the concrete type. + if aIsIntf { + a, b = b, a + } + + return types.AssignableTo(a, b) +} + +// ensurePointer wraps T in a *types.Pointer if T is a named, non-interface +// type. This is useful to make sure you consider a named type's full method +// set. +func ensurePointer(T types.Type) types.Type { + if _, ok := T.(*types.Named); ok && !IsInterface(T) { + return types.NewPointer(T) + } + + return T +} + +// A qualifiedObject is the result of resolving a reference from an +// identifier to an object. +type qualifiedObject struct { + // definition + obj types.Object // the referenced object + pkg Package // the Package that defines the object (nil => universe) + + // reference (optional) + node ast.Node // the reference (*ast.Ident or *ast.ImportSpec) to the object + sourcePkg Package // the Package containing node +} + +var ( + errBuiltin = errors.New("builtin object") + errNoObjectFound = errors.New("no object found") +) + +// qualifiedObjsAtProtocolPos returns info for all the types.Objects referenced +// at the given position, for the following selection of packages: +// +// 1. all packages (including all test variants), in their workspace parse mode +// 2. if not included above, at least one package containing uri in full parse mode +// +// Finding objects in (1) ensures that we locate references within all +// workspace packages, including in x_test packages. Including (2) ensures that +// we find local references in the current package, for non-workspace packages +// that may be open. +func qualifiedObjsAtProtocolPos(ctx context.Context, s Snapshot, uri span.URI, pp protocol.Position) ([]qualifiedObject, error) { + fh, err := s.GetFile(ctx, uri) + if err != nil { + return nil, err + } + content, err := fh.Read() + if err != nil { + return nil, err + } + m := protocol.NewColumnMapper(uri, content) + offset, err := m.Offset(pp) + if err != nil { + return nil, err + } + return qualifiedObjsAtLocation(ctx, s, positionKey{uri, offset}, map[positionKey]bool{}) +} + +// A positionKey identifies a byte offset within a file (URI). +// +// When a file has been parsed multiple times in the same FileSet, +// there may be multiple token.Pos values denoting the same logical +// position. In such situations, a positionKey may be used for +// de-duplication. +type positionKey struct { + uri span.URI + offset int +} + +// qualifiedObjsAtLocation finds all objects referenced at offset in uri, +// across all packages in the snapshot. +func qualifiedObjsAtLocation(ctx context.Context, s Snapshot, key positionKey, seen map[positionKey]bool) ([]qualifiedObject, error) { + if seen[key] { + return nil, nil + } + seen[key] = true + + // We search for referenced objects starting with all packages containing the + // current location, and then repeating the search for every distinct object + // location discovered. + // + // In the common case, there should be at most one additional location to + // consider: the definition of the object referenced by the location. But we + // try to be comprehensive in case we ever support variations on build + // constraints. + + pkgs, err := s.PackagesForFile(ctx, key.uri, TypecheckWorkspace, true) + if err != nil { + return nil, err + } + + // In order to allow basic references/rename/implementations to function when + // non-workspace packages are open, ensure that we have at least one fully + // parsed package for the current file. This allows us to find references + // inside the open package. Use WidestPackage to capture references in test + // files. + hasFullPackage := false + for _, pkg := range pkgs { + if pkg.ParseMode() == ParseFull { + hasFullPackage = true + break + } + } + if !hasFullPackage { + pkg, err := s.PackageForFile(ctx, key.uri, TypecheckFull, WidestPackage) + if err != nil { + return nil, err + } + pkgs = append(pkgs, pkg) + } + + // report objects in the order we encounter them. This ensures that the first + // result is at the cursor... + var qualifiedObjs []qualifiedObject + // ...but avoid duplicates. + seenObjs := map[types.Object]bool{} + + for _, searchpkg := range pkgs { + pgf, err := searchpkg.File(key.uri) + if err != nil { + return nil, err + } + pos := pgf.Tok.Pos(key.offset) + path := pathEnclosingObjNode(pgf.File, pos) + if path == nil { + continue + } + var objs []types.Object + switch leaf := path[0].(type) { + case *ast.Ident: + // If leaf represents an implicit type switch object or the type + // switch "assign" variable, expand to all of the type switch's + // implicit objects. + if implicits, _ := typeSwitchImplicits(searchpkg, path); len(implicits) > 0 { + objs = append(objs, implicits...) + } else { + obj := searchpkg.GetTypesInfo().ObjectOf(leaf) + if obj == nil { + return nil, fmt.Errorf("%w for %q", errNoObjectFound, leaf.Name) + } + objs = append(objs, obj) + } + case *ast.ImportSpec: + // Look up the implicit *types.PkgName. + obj := searchpkg.GetTypesInfo().Implicits[leaf] + if obj == nil { + return nil, fmt.Errorf("%w for import %s", errNoObjectFound, UnquoteImportPath(leaf)) + } + objs = append(objs, obj) + } + // Get all of the transitive dependencies of the search package. + pkgs := make(map[*types.Package]Package) + var addPkg func(pkg Package) + addPkg = func(pkg Package) { + pkgs[pkg.GetTypes()] = pkg + for _, imp := range pkg.Imports() { + if _, ok := pkgs[imp.GetTypes()]; !ok { + addPkg(imp) + } + } + } + addPkg(searchpkg) + for _, obj := range objs { + if obj.Parent() == types.Universe { + return nil, fmt.Errorf("%q: %w", obj.Name(), errBuiltin) + } + pkg, ok := pkgs[obj.Pkg()] + if !ok { + event.Error(ctx, fmt.Sprintf("no package for obj %s: %v", obj, obj.Pkg()), err) + continue + } + qualifiedObjs = append(qualifiedObjs, qualifiedObject{ + obj: obj, + pkg: pkg, + sourcePkg: searchpkg, + node: path[0], + }) + seenObjs[obj] = true + + // If the qualified object is in another file (or more likely, another + // package), it's possible that there is another copy of it in a package + // that we haven't searched, e.g. a test variant. See golang/go#47564. + // + // In order to be sure we've considered all packages, call + // qualifiedObjsAtLocation recursively for all locations we encounter. We + // could probably be more precise here, only continuing the search if obj + // is in another package, but this should be good enough to find all + // uses. + + if key, found := packagePositionKey(pkg, obj.Pos()); found { + otherObjs, err := qualifiedObjsAtLocation(ctx, s, key, seen) + if err != nil { + return nil, err + } + for _, other := range otherObjs { + if !seenObjs[other.obj] { + qualifiedObjs = append(qualifiedObjs, other) + seenObjs[other.obj] = true + } + } + } else { + return nil, fmt.Errorf("missing file for position of %q in %q", obj.Name(), obj.Pkg().Name()) + } + } + } + // Return an error if no objects were found since callers will assume that + // the slice has at least 1 element. + if len(qualifiedObjs) == 0 { + return nil, errNoObjectFound + } + return qualifiedObjs, nil +} + +// packagePositionKey finds the positionKey for the given pos. +// +// The second result reports whether the position was found. +func packagePositionKey(pkg Package, pos token.Pos) (positionKey, bool) { + for _, pgf := range pkg.CompiledGoFiles() { + offset, err := safetoken.Offset(pgf.Tok, pos) + if err == nil { + return positionKey{pgf.URI, offset}, true + } + } + return positionKey{}, false +} + +// pathEnclosingObjNode returns the AST path to the object-defining +// node associated with pos. "Object-defining" means either an +// *ast.Ident mapped directly to a types.Object or an ast.Node mapped +// implicitly to a types.Object. +func pathEnclosingObjNode(f *ast.File, pos token.Pos) []ast.Node { + var ( + path []ast.Node + found bool + ) + + ast.Inspect(f, func(n ast.Node) bool { + if found { + return false + } + + if n == nil { + path = path[:len(path)-1] + return false + } + + path = append(path, n) + + switch n := n.(type) { + case *ast.Ident: + // Include the position directly after identifier. This handles + // the common case where the cursor is right after the + // identifier the user is currently typing. Previously we + // handled this by calling astutil.PathEnclosingInterval twice, + // once for "pos" and once for "pos-1". + found = n.Pos() <= pos && pos <= n.End() + case *ast.ImportSpec: + if n.Path.Pos() <= pos && pos < n.Path.End() { + found = true + // If import spec has a name, add name to path even though + // position isn't in the name. + if n.Name != nil { + path = append(path, n.Name) + } + } + case *ast.StarExpr: + // Follow star expressions to the inner identifier. + if pos == n.Star { + pos = n.X.Pos() + } + } + + return !found + }) + + if len(path) == 0 { + return nil + } + + // Reverse path so leaf is first element. + for i := 0; i < len(path)/2; i++ { + path[i], path[len(path)-1-i] = path[len(path)-1-i], path[i] + } + + return path +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/inlay_hint.go b/gopls/golang_org_x_tools_gopls/lsp/source/inlay_hint.go new file mode 100644 index 0000000..86da7d3 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/inlay_hint.go @@ -0,0 +1,396 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "fmt" + "go/ast" + "go/constant" + "go/token" + "go/types" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/lsppos" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +const ( + maxLabelLength = 28 +) + +type InlayHintFunc func(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint + +type Hint struct { + Name string + Doc string + Run InlayHintFunc +} + +const ( + ParameterNames = "parameterNames" + AssignVariableTypes = "assignVariableTypes" + ConstantValues = "constantValues" + RangeVariableTypes = "rangeVariableTypes" + CompositeLiteralTypes = "compositeLiteralTypes" + CompositeLiteralFieldNames = "compositeLiteralFields" + FunctionTypeParameters = "functionTypeParameters" +) + +var AllInlayHints = map[string]*Hint{ + AssignVariableTypes: { + Name: AssignVariableTypes, + Doc: "Enable/disable inlay hints for variable types in assign statements:\n```go\n\ti/* int*/, j/* int*/ := 0, len(r)-1\n```", + Run: assignVariableTypes, + }, + ParameterNames: { + Name: ParameterNames, + Doc: "Enable/disable inlay hints for parameter names:\n```go\n\tparseInt(/* str: */ \"123\", /* radix: */ 8)\n```", + Run: parameterNames, + }, + ConstantValues: { + Name: ConstantValues, + Doc: "Enable/disable inlay hints for constant values:\n```go\n\tconst (\n\t\tKindNone Kind = iota/* = 0*/\n\t\tKindPrint/* = 1*/\n\t\tKindPrintf/* = 2*/\n\t\tKindErrorf/* = 3*/\n\t)\n```", + Run: constantValues, + }, + RangeVariableTypes: { + Name: RangeVariableTypes, + Doc: "Enable/disable inlay hints for variable types in range statements:\n```go\n\tfor k/* int*/, v/* string*/ := range []string{} {\n\t\tfmt.Println(k, v)\n\t}\n```", + Run: rangeVariableTypes, + }, + CompositeLiteralTypes: { + Name: CompositeLiteralTypes, + Doc: "Enable/disable inlay hints for composite literal types:\n```go\n\tfor _, c := range []struct {\n\t\tin, want string\n\t}{\n\t\t/*struct{ in string; want string }*/{\"Hello, world\", \"dlrow ,olleH\"},\n\t}\n```", + Run: compositeLiteralTypes, + }, + CompositeLiteralFieldNames: { + Name: CompositeLiteralFieldNames, + Doc: "Enable/disable inlay hints for composite literal field names:\n```go\n\t{/*in: */\"Hello, world\", /*want: */\"dlrow ,olleH\"}\n```", + Run: compositeLiteralFields, + }, + FunctionTypeParameters: { + Name: FunctionTypeParameters, + Doc: "Enable/disable inlay hints for implicit type parameters on generic functions:\n```go\n\tmyFoo/*[int, string]*/(1, \"hello\")\n```", + Run: funcTypeParams, + }, +} + +func InlayHint(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) ([]protocol.InlayHint, error) { + ctx, done := event.Start(ctx, "source.InlayHint") + defer done() + + pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return nil, fmt.Errorf("getting file for InlayHint: %w", err) + } + + // Collect a list of the inlay hints that are enabled. + inlayHintOptions := snapshot.View().Options().InlayHintOptions + var enabledHints []InlayHintFunc + for hint, enabled := range inlayHintOptions.Hints { + if !enabled { + continue + } + if h, ok := AllInlayHints[hint]; ok { + enabledHints = append(enabledHints, h.Run) + } + } + if len(enabledHints) == 0 { + return nil, nil + } + + tmap := lsppos.NewTokenMapper(pgf.Src, pgf.Tok) + info := pkg.GetTypesInfo() + q := Qualifier(pgf.File, pkg.GetTypes(), info) + + // Set the range to the full file if the range is not valid. + start, end := pgf.File.Pos(), pgf.File.End() + if pRng.Start.Line < pRng.End.Line || pRng.Start.Character < pRng.End.Character { + // Adjust start and end for the specified range. + rng, err := pgf.Mapper.RangeToSpanRange(pRng) + if err != nil { + return nil, err + } + start, end = rng.Start, rng.End + } + + var hints []protocol.InlayHint + ast.Inspect(pgf.File, func(node ast.Node) bool { + // If not in range, we can stop looking. + if node == nil || node.End() < start || node.Pos() > end { + return false + } + for _, fn := range enabledHints { + hints = append(hints, fn(node, tmap, info, &q)...) + } + return true + }) + return hints, nil +} + +func parameterNames(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, _ *types.Qualifier) []protocol.InlayHint { + callExpr, ok := node.(*ast.CallExpr) + if !ok { + return nil + } + signature, ok := info.TypeOf(callExpr.Fun).(*types.Signature) + if !ok { + return nil + } + + var hints []protocol.InlayHint + for i, v := range callExpr.Args { + start, ok := tmap.Position(v.Pos()) + if !ok { + continue + } + params := signature.Params() + // When a function has variadic params, we skip args after + // params.Len(). + if i > params.Len()-1 { + break + } + param := params.At(i) + // param.Name is empty for built-ins like append + if param.Name() == "" { + continue + } + // Skip the parameter name hint if the arg matches the + // the parameter name. + if i, ok := v.(*ast.Ident); ok && i.Name == param.Name() { + continue + } + + label := param.Name() + if signature.Variadic() && i == params.Len()-1 { + label = label + "..." + } + hints = append(hints, protocol.InlayHint{ + Position: &start, + Label: buildLabel(label + ":"), + Kind: protocol.Parameter, + PaddingRight: true, + }) + } + return hints +} + +func funcTypeParams(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, _ *types.Qualifier) []protocol.InlayHint { + ce, ok := node.(*ast.CallExpr) + if !ok { + return nil + } + id, ok := ce.Fun.(*ast.Ident) + if !ok { + return nil + } + inst := typeparams.GetInstances(info)[id] + if inst.TypeArgs == nil { + return nil + } + start, ok := tmap.Position(id.End()) + if !ok { + return nil + } + var args []string + for i := 0; i < inst.TypeArgs.Len(); i++ { + args = append(args, inst.TypeArgs.At(i).String()) + } + if len(args) == 0 { + return nil + } + return []protocol.InlayHint{{ + Position: &start, + Label: buildLabel("[" + strings.Join(args, ", ") + "]"), + Kind: protocol.Type, + }} +} + +func assignVariableTypes(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + stmt, ok := node.(*ast.AssignStmt) + if !ok || stmt.Tok != token.DEFINE { + return nil + } + + var hints []protocol.InlayHint + for _, v := range stmt.Lhs { + if h := variableType(v, tmap, info, q); h != nil { + hints = append(hints, *h) + } + } + return hints +} + +func rangeVariableTypes(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + rStmt, ok := node.(*ast.RangeStmt) + if !ok { + return nil + } + var hints []protocol.InlayHint + if h := variableType(rStmt.Key, tmap, info, q); h != nil { + hints = append(hints, *h) + } + if h := variableType(rStmt.Value, tmap, info, q); h != nil { + hints = append(hints, *h) + } + return hints +} + +func variableType(e ast.Expr, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) *protocol.InlayHint { + typ := info.TypeOf(e) + if typ == nil { + return nil + } + end, ok := tmap.Position(e.End()) + if !ok { + return nil + } + return &protocol.InlayHint{ + Position: &end, + Label: buildLabel(types.TypeString(typ, *q)), + Kind: protocol.Type, + PaddingLeft: true, + } +} + +func constantValues(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, _ *types.Qualifier) []protocol.InlayHint { + genDecl, ok := node.(*ast.GenDecl) + if !ok || genDecl.Tok != token.CONST { + return nil + } + + var hints []protocol.InlayHint + for _, v := range genDecl.Specs { + spec, ok := v.(*ast.ValueSpec) + if !ok { + continue + } + end, ok := tmap.Position(v.End()) + if !ok { + continue + } + // Show hints when values are missing or at least one value is not + // a basic literal. + showHints := len(spec.Values) == 0 + checkValues := len(spec.Names) == len(spec.Values) + var values []string + for i, w := range spec.Names { + obj, ok := info.ObjectOf(w).(*types.Const) + if !ok || obj.Val().Kind() == constant.Unknown { + return nil + } + if checkValues { + switch spec.Values[i].(type) { + case *ast.BadExpr: + return nil + case *ast.BasicLit: + default: + if obj.Val().Kind() != constant.Bool { + showHints = true + } + } + } + values = append(values, fmt.Sprintf("%v", obj.Val())) + } + if !showHints || len(values) == 0 { + continue + } + hints = append(hints, protocol.InlayHint{ + Position: &end, + Label: buildLabel("= " + strings.Join(values, ", ")), + PaddingLeft: true, + }) + } + return hints +} + +func compositeLiteralFields(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + compLit, ok := node.(*ast.CompositeLit) + if !ok { + return nil + } + typ := info.TypeOf(compLit) + if typ == nil { + return nil + } + if t, ok := typ.(*types.Pointer); ok { + typ = t.Elem() + } + strct, ok := typ.Underlying().(*types.Struct) + if !ok { + return nil + } + + var hints []protocol.InlayHint + var allEdits []protocol.TextEdit + for i, v := range compLit.Elts { + if _, ok := v.(*ast.KeyValueExpr); !ok { + start, ok := tmap.Position(v.Pos()) + if !ok { + continue + } + if i > strct.NumFields()-1 { + break + } + hints = append(hints, protocol.InlayHint{ + Position: &start, + Label: buildLabel(strct.Field(i).Name() + ":"), + Kind: protocol.Parameter, + PaddingRight: true, + }) + allEdits = append(allEdits, protocol.TextEdit{ + Range: protocol.Range{Start: start, End: start}, + NewText: strct.Field(i).Name() + ": ", + }) + } + } + // It is not allowed to have a mix of keyed and unkeyed fields, so + // have the text edits add keys to all fields. + for i := range hints { + hints[i].TextEdits = allEdits + } + return hints +} + +func compositeLiteralTypes(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + compLit, ok := node.(*ast.CompositeLit) + if !ok { + return nil + } + typ := info.TypeOf(compLit) + if typ == nil { + return nil + } + if compLit.Type != nil { + return nil + } + prefix := "" + if t, ok := typ.(*types.Pointer); ok { + typ = t.Elem() + prefix = "&" + } + // The type for this composite literal is implicit, add an inlay hint. + start, ok := tmap.Position(compLit.Lbrace) + if !ok { + return nil + } + return []protocol.InlayHint{{ + Position: &start, + Label: buildLabel(fmt.Sprintf("%s%s", prefix, types.TypeString(typ, *q))), + Kind: protocol.Type, + }} +} + +func buildLabel(s string) []protocol.InlayHintLabelPart { + label := protocol.InlayHintLabelPart{ + Value: s, + } + if len(s) > maxLabelLength+len("...") { + label.Value = s[:maxLabelLength] + "..." + } + return []protocol.InlayHintLabelPart{label} +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/known_packages.go b/gopls/golang_org_x_tools_gopls/lsp/source/known_packages.go new file mode 100644 index 0000000..71eb293 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/known_packages.go @@ -0,0 +1,123 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "fmt" + "sort" + "strings" + "sync" + "time" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/imports" +) + +// KnownPackages returns a list of all known packages +// in the package graph that could potentially be imported +// by the given file. +func KnownPackages(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle) ([]PackagePath, error) { + pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return nil, fmt.Errorf("GetParsedFile: %w", err) + } + alreadyImported := map[PackagePath]struct{}{} + for _, imp := range pgf.File.Imports { + // TODO(adonovan): the correct PackagePath might need a "vendor/" prefix. + alreadyImported[PackagePath(imp.Path.Value)] = struct{}{} + } + // TODO(adonovan): this whole algorithm could be more + // simply expressed in terms of Metadata, not Packages. + pkgs, err := snapshot.CachedImportPaths(ctx) + if err != nil { + return nil, err + } + var ( + seen = make(map[PackagePath]struct{}) + paths []PackagePath + ) + for path, knownPkg := range pkgs { + gofiles := knownPkg.CompiledGoFiles() + if len(gofiles) == 0 || gofiles[0].File.Name == nil { + continue + } + pkgName := gofiles[0].File.Name.Name + // package main cannot be imported + if pkgName == "main" { + continue + } + // test packages cannot be imported + if knownPkg.ForTest() != "" { + continue + } + // no need to import what the file already imports + if _, ok := alreadyImported[path]; ok { + continue + } + // snapshot.KnownPackages could have multiple versions of a pkg + if _, ok := seen[path]; ok { + continue + } + seen[path] = struct{}{} + // make sure internal packages are importable by the file + if !IsValidImport(pkg.PkgPath(), path) { + continue + } + // naive check on cyclical imports + if isDirectlyCyclical(pkg, knownPkg) { + continue + } + paths = append(paths, path) + seen[path] = struct{}{} + } + err = snapshot.RunProcessEnvFunc(ctx, func(o *imports.Options) error { + var mu sync.Mutex + ctx, cancel := context.WithTimeout(ctx, time.Millisecond*80) + defer cancel() + return imports.GetAllCandidates(ctx, func(ifix imports.ImportFix) { + mu.Lock() + defer mu.Unlock() + // TODO(adonovan): what if the actual package path has a vendor/ prefix? + path := PackagePath(ifix.StmtInfo.ImportPath) + if _, ok := seen[path]; ok { + return + } + paths = append(paths, path) + }, "", pgf.URI.Filename(), pkg.GetTypes().Name(), o.Env) + }) + if err != nil { + // if an error occurred, we still have a decent list we can + // show to the user through snapshot.CachedImportPaths + event.Error(ctx, "imports.GetAllCandidates", err) + } + sort.Slice(paths, func(i, j int) bool { + importI, importJ := paths[i], paths[j] + iHasDot := strings.Contains(string(importI), ".") + jHasDot := strings.Contains(string(importJ), ".") + if iHasDot && !jHasDot { + return false + } + if jHasDot && !iHasDot { + return true + } + return importI < importJ + }) + return paths, nil +} + +// isDirectlyCyclical checks if imported directly imports pkg. +// It does not (yet) offer a full cyclical check because showing a user +// a list of importable packages already generates a very large list +// and having a few false positives in there could be worth the +// performance snappiness. +func isDirectlyCyclical(pkg, imported Package) bool { + for _, imp := range imported.Imports() { + if imp.PkgPath() == pkg.PkgPath() { + return true + } + } + return false +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/options.go b/gopls/golang_org_x_tools_gopls/lsp/source/options.go new file mode 100644 index 0000000..9bff353 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/options.go @@ -0,0 +1,1673 @@ +// 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 source + +import ( + "context" + "fmt" + "io" + "path/filepath" + "regexp" + "runtime" + "strings" + "sync" + "time" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/asmdecl" + "golang.org/x/tools/go/analysis/passes/assign" + "golang.org/x/tools/go/analysis/passes/atomic" + "golang.org/x/tools/go/analysis/passes/atomicalign" + "golang.org/x/tools/go/analysis/passes/bools" + "golang.org/x/tools/go/analysis/passes/buildtag" + "golang.org/x/tools/go/analysis/passes/cgocall" + "golang.org/x/tools/go/analysis/passes/composite" + "golang.org/x/tools/go/analysis/passes/copylock" + "golang.org/x/tools/go/analysis/passes/deepequalerrors" + "golang.org/x/tools/go/analysis/passes/errorsas" + "golang.org/x/tools/go/analysis/passes/fieldalignment" + "golang.org/x/tools/go/analysis/passes/httpresponse" + "golang.org/x/tools/go/analysis/passes/ifaceassert" + "golang.org/x/tools/go/analysis/passes/loopclosure" + "golang.org/x/tools/go/analysis/passes/lostcancel" + "golang.org/x/tools/go/analysis/passes/nilfunc" + "golang.org/x/tools/go/analysis/passes/nilness" + "golang.org/x/tools/go/analysis/passes/printf" + "golang.org/x/tools/go/analysis/passes/shadow" + "golang.org/x/tools/go/analysis/passes/shift" + "golang.org/x/tools/go/analysis/passes/sortslice" + "golang.org/x/tools/go/analysis/passes/stdmethods" + "golang.org/x/tools/go/analysis/passes/stringintconv" + "golang.org/x/tools/go/analysis/passes/structtag" + "golang.org/x/tools/go/analysis/passes/testinggoroutine" + "golang.org/x/tools/go/analysis/passes/tests" + "golang.org/x/tools/go/analysis/passes/timeformat" + "golang.org/x/tools/go/analysis/passes/unmarshal" + "golang.org/x/tools/go/analysis/passes/unreachable" + "golang.org/x/tools/go/analysis/passes/unsafeptr" + "golang.org/x/tools/go/analysis/passes/unusedresult" + "golang.org/x/tools/go/analysis/passes/unusedwrite" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/embeddirective" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/fillreturns" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/fillstruct" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/infertypeargs" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/nonewvars" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/noresultvalues" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifycompositelit" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyrange" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/simplifyslice" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/stubmethods" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/undeclaredname" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedparams" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/unusedvariable" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/useany" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/command" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/diff" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/diff/myers" +) + +var ( + optionsOnce sync.Once + defaultOptions *Options +) + +// DefaultOptions is the options that are used for Gopls execution independent +// of any externally provided configuration (LSP initialization, command +// invocation, etc.). +func DefaultOptions() *Options { + optionsOnce.Do(func() { + var commands []string + for _, c := range command.Commands { + commands = append(commands, c.ID()) + } + defaultOptions = &Options{ + ClientOptions: ClientOptions{ + InsertTextFormat: protocol.PlainTextTextFormat, + PreferredContentFormat: protocol.Markdown, + ConfigurationSupported: true, + DynamicConfigurationSupported: true, + DynamicRegistrationSemanticTokensSupported: true, + DynamicWatchedFilesSupported: true, + LineFoldingOnly: false, + HierarchicalDocumentSymbolSupport: true, + }, + ServerOptions: ServerOptions{ + SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{ + Go: { + protocol.SourceFixAll: true, + protocol.SourceOrganizeImports: true, + protocol.QuickFix: true, + protocol.RefactorRewrite: true, + protocol.RefactorExtract: true, + }, + Mod: { + protocol.SourceOrganizeImports: true, + protocol.QuickFix: true, + }, + Work: {}, + Sum: {}, + Tmpl: {}, + }, + SupportedCommands: commands, + }, + UserOptions: UserOptions{ + BuildOptions: BuildOptions{ + ExpandWorkspaceToModule: true, + ExperimentalPackageCacheKey: true, + MemoryMode: ModeNormal, + DirectoryFilters: []string{"-**/node_modules"}, + TemplateExtensions: []string{}, + StandaloneTags: []string{"ignore"}, + }, + UIOptions: UIOptions{ + DiagnosticOptions: DiagnosticOptions{ + DiagnosticsDelay: 250 * time.Millisecond, + Annotations: map[Annotation]bool{ + Bounds: true, + Escape: true, + Inline: true, + Nil: true, + }, + }, + InlayHintOptions: InlayHintOptions{}, + DocumentationOptions: DocumentationOptions{ + HoverKind: FullDocumentation, + LinkTarget: "pkg.go.dev", + LinksInHover: true, + }, + NavigationOptions: NavigationOptions{ + ImportShortcut: Both, + SymbolMatcher: SymbolFastFuzzy, + SymbolStyle: DynamicSymbols, + }, + CompletionOptions: CompletionOptions{ + Matcher: Fuzzy, + CompletionBudget: 100 * time.Millisecond, + ExperimentalPostfixCompletions: true, + }, + Codelenses: map[string]bool{ + string(command.Generate): true, + string(command.RegenerateCgo): true, + string(command.Tidy): true, + string(command.GCDetails): false, + string(command.UpgradeDependency): true, + string(command.Vendor): true, + // TODO(hyangah): enable command.RunVulncheckExp. + }, + }, + }, + InternalOptions: InternalOptions{ + LiteralCompletions: true, + TempModfile: true, + CompleteUnimported: true, + CompletionDocumentation: true, + DeepCompletion: true, + ChattyDiagnostics: true, + NewDiff: "both", + }, + Hooks: Hooks{ + // TODO(adonovan): switch to new diff.Strings implementation. + ComputeEdits: myers.ComputeEdits, + URLRegexp: urlRegexp(), + DefaultAnalyzers: defaultAnalyzers(), + TypeErrorAnalyzers: typeErrorAnalyzers(), + ConvenienceAnalyzers: convenienceAnalyzers(), + StaticcheckAnalyzers: map[string]*Analyzer{}, + GoDiff: true, + }, + } + }) + return defaultOptions +} + +// Options holds various configuration that affects Gopls execution, organized +// by the nature or origin of the settings. +type Options struct { + ClientOptions + ServerOptions + UserOptions + InternalOptions + Hooks +} + +// ClientOptions holds LSP-specific configuration that is provided by the +// client. +type ClientOptions struct { + InsertTextFormat protocol.InsertTextFormat + ConfigurationSupported bool + DynamicConfigurationSupported bool + DynamicRegistrationSemanticTokensSupported bool + DynamicWatchedFilesSupported bool + PreferredContentFormat protocol.MarkupKind + LineFoldingOnly bool + HierarchicalDocumentSymbolSupport bool + SemanticTypes []string + SemanticMods []string + RelatedInformationSupported bool + CompletionTags bool + CompletionDeprecated bool + SupportedResourceOperations []protocol.ResourceOperationKind +} + +// ServerOptions holds LSP-specific configuration that is provided by the +// server. +type ServerOptions struct { + SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool + SupportedCommands []string +} + +type BuildOptions struct { + // BuildFlags is the set of flags passed on to the build system when invoked. + // It is applied to queries like `go list`, which is used when discovering files. + // The most common use is to set `-tags`. + BuildFlags []string + + // Env adds environment variables to external commands run by `gopls`, most notably `go list`. + Env map[string]string + + // DirectoryFilters can be used to exclude unwanted directories from the + // workspace. By default, all directories are included. Filters are an + // operator, `+` to include and `-` to exclude, followed by a path prefix + // relative to the workspace folder. They are evaluated in order, and + // the last filter that applies to a path controls whether it is included. + // The path prefix can be empty, so an initial `-` excludes everything. + // + // DirectoryFilters also supports the `**` operator to match 0 or more directories. + // + // Examples: + // + // Exclude node_modules at current depth: `-node_modules` + // + // Exclude node_modules at any depth: `-**/node_modules` + // + // Include only project_a: `-` (exclude everything), `+project_a` + // + // Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules` + DirectoryFilters []string + + // TemplateExtensions gives the extensions of file names that are treateed + // as template files. (The extension + // is the part of the file name after the final dot.) + TemplateExtensions []string + + // MemoryMode controls the tradeoff `gopls` makes between memory usage and + // correctness. + // + // Values other than `Normal` are untested and may break in surprising ways. + MemoryMode MemoryMode `status:"experimental"` + + // ExpandWorkspaceToModule instructs `gopls` to adjust the scope of the + // workspace to find the best available module root. `gopls` first looks for + // a go.mod file in any parent directory of the workspace folder, expanding + // the scope to that directory if it exists. If no viable parent directory is + // found, gopls will check if there is exactly one child directory containing + // a go.mod file, narrowing the scope to that directory if it exists. + ExpandWorkspaceToModule bool `status:"experimental"` + + // ExperimentalWorkspaceModule opts a user into the experimental support + // for multi-module workspaces. + // + // Deprecated: this feature is deprecated and will be removed in a future + // version of gopls (https://go.dev/issue/55331). + ExperimentalWorkspaceModule bool `status:"experimental"` + + // ExperimentalPackageCacheKey controls whether to use a coarser cache key + // for package type information to increase cache hits. This setting removes + // the user's environment, build flags, and working directory from the cache + // key, which should be a safe change as all relevant inputs into the type + // checking pass are already hashed into the key. This is temporarily guarded + // by an experiment because caching behavior is subtle and difficult to + // comprehensively test. + ExperimentalPackageCacheKey bool `status:"experimental"` + + // AllowModfileModifications disables -mod=readonly, allowing imports from + // out-of-scope modules. This option will eventually be removed. + AllowModfileModifications bool `status:"experimental"` + + // AllowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module + // downloads rather than requiring user action. This option will eventually + // be removed. + AllowImplicitNetworkAccess bool `status:"experimental"` + + // ExperimentalUseInvalidMetadata enables gopls to fall back on outdated + // package metadata to provide editor features if the go command fails to + // load packages for some reason (like an invalid go.mod file). + // + // Deprecated: this setting is deprecated and will be removed in a future + // version of gopls (https://go.dev/issue/55333). + ExperimentalUseInvalidMetadata bool `status:"experimental"` + + // StandaloneTags specifies a set of build constraints that identify + // individual Go source files that make up the entire main package of an + // executable. + // + // A common example of standalone main files is the convention of using the + // directive `//go:build ignore` to denote files that are not intended to be + // included in any package, for example because they are invoked directly by + // the developer using `go run`. + // + // Gopls considers a file to be a standalone main file if and only if it has + // package name "main" and has a build directive of the exact form + // "//go:build tag" or "// +build tag", where tag is among the list of tags + // configured by this setting. Notably, if the build constraint is more + // complicated than a simple tag (such as the composite constraint + // `//go:build tag && go1.18`), the file is not considered to be a standalone + // main file. + // + // This setting is only supported when gopls is built with Go 1.16 or later. + StandaloneTags []string +} + +type UIOptions struct { + DocumentationOptions + CompletionOptions + NavigationOptions + DiagnosticOptions + InlayHintOptions + + // Codelenses overrides the enabled/disabled state of code lenses. See the + // "Code Lenses" section of the + // [Settings page](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#code-lenses) + // for the list of supported lenses. + // + // Example Usage: + // + // ```json5 + // "gopls": { + // ... + // "codelenses": { + // "generate": false, // Don't show the `go generate` lens. + // "gc_details": true // Show a code lens toggling the display of gc's choices. + // } + // ... + // } + // ``` + Codelenses map[string]bool + + // SemanticTokens controls whether the LSP server will send + // semantic tokens to the client. + SemanticTokens bool `status:"experimental"` + + // NoSemanticString turns off the sending of the semantic token 'string' + NoSemanticString bool `status:"experimental"` + + // NoSemanticNumber turns off the sending of the semantic token 'number' + NoSemanticNumber bool `status:"experimental"` +} + +type CompletionOptions struct { + // Placeholders enables placeholders for function parameters or struct + // fields in completion responses. + UsePlaceholders bool + + // CompletionBudget is the soft latency goal for completion requests. Most + // requests finish in a couple milliseconds, but in some cases deep + // completions can take much longer. As we use up our budget we + // dynamically reduce the search scope to ensure we return timely + // results. Zero means unlimited. + CompletionBudget time.Duration `status:"debug"` + + // Matcher sets the algorithm that is used when calculating completion + // candidates. + Matcher Matcher `status:"advanced"` + + // ExperimentalPostfixCompletions enables artificial method snippets + // such as "someSlice.sort!". + ExperimentalPostfixCompletions bool `status:"experimental"` +} + +type DocumentationOptions struct { + // HoverKind controls the information that appears in the hover text. + // SingleLine and Structured are intended for use only by authors of editor plugins. + HoverKind HoverKind + + // LinkTarget controls where documentation links go. + // It might be one of: + // + // * `"godoc.org"` + // * `"pkg.go.dev"` + // + // If company chooses to use its own `godoc.org`, its address can be used as well. + // + // Modules matching the GOPRIVATE environment variable will not have + // documentation links in hover. + LinkTarget string + + // LinksInHover toggles the presence of links to documentation in hover. + LinksInHover bool +} + +type FormattingOptions struct { + // Local is the equivalent of the `goimports -local` flag, which puts + // imports beginning with this string after third-party packages. It should + // be the prefix of the import path whose imports should be grouped + // separately. + Local string + + // Gofumpt indicates if we should run gofumpt formatting. + Gofumpt bool +} + +type DiagnosticOptions struct { + // Analyses specify analyses that the user would like to enable or disable. + // A map of the names of analysis passes that should be enabled/disabled. + // A full list of analyzers that gopls uses can be found in + // [analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md). + // + // Example Usage: + // + // ```json5 + // ... + // "analyses": { + // "unreachable": false, // Disable the unreachable analyzer. + // "unusedparams": true // Enable the unusedparams analyzer. + // } + // ... + // ``` + Analyses map[string]bool + + // Staticcheck enables additional analyses from staticcheck.io. + // These analyses are documented on + // [Staticcheck's website](https://staticcheck.io/docs/checks/). + Staticcheck bool `status:"experimental"` + + // Annotations specifies the various kinds of optimization diagnostics + // that should be reported by the gc_details command. + Annotations map[Annotation]bool `status:"experimental"` + + // DiagnosticsDelay controls the amount of time that gopls waits + // after the most recent file modification before computing deep diagnostics. + // Simple diagnostics (parsing and type-checking) are always run immediately + // on recently modified packages. + // + // This option must be set to a valid duration string, for example `"250ms"`. + DiagnosticsDelay time.Duration `status:"advanced"` + + // ExperimentalWatchedFileDelay controls the amount of time that gopls waits + // for additional workspace/didChangeWatchedFiles notifications to arrive, + // before processing all such notifications in a single batch. This is + // intended for use by LSP clients that don't support their own batching of + // file system notifications. + // + // This option must be set to a valid duration string, for example `"100ms"`. + // + // Deprecated: this setting is deprecated and will be removed in a future + // version of gopls (https://go.dev/issue/55332) + ExperimentalWatchedFileDelay time.Duration `status:"experimental"` +} + +type InlayHintOptions struct { + // Hints specify inlay hints that users want to see. A full list of hints + // that gopls uses can be found in + // [inlayHints.md](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md). + Hints map[string]bool `status:"experimental"` +} + +type NavigationOptions struct { + // ImportShortcut specifies whether import statements should link to + // documentation or go to definitions. + ImportShortcut ImportShortcut + + // SymbolMatcher sets the algorithm that is used when finding workspace symbols. + SymbolMatcher SymbolMatcher `status:"advanced"` + + // SymbolStyle controls how symbols are qualified in symbol responses. + // + // Example Usage: + // + // ```json5 + // "gopls": { + // ... + // "symbolStyle": "Dynamic", + // ... + // } + // ``` + SymbolStyle SymbolStyle `status:"advanced"` +} + +// UserOptions holds custom Gopls configuration (not part of the LSP) that is +// modified by the client. +type UserOptions struct { + BuildOptions + UIOptions + FormattingOptions + + // VerboseOutput enables additional debug logging. + VerboseOutput bool `status:"debug"` +} + +// EnvSlice returns Env as a slice of k=v strings. +func (u *UserOptions) EnvSlice() []string { + var result []string + for k, v := range u.Env { + result = append(result, fmt.Sprintf("%v=%v", k, v)) + } + return result +} + +// SetEnvSlice sets Env from a slice of k=v strings. +func (u *UserOptions) SetEnvSlice(env []string) { + u.Env = map[string]string{} + for _, kv := range env { + split := strings.SplitN(kv, "=", 2) + if len(split) != 2 { + continue + } + u.Env[split[0]] = split[1] + } +} + +// DiffFunction is the type for a function that produces a set of edits that +// convert from the before content to the after content. +type DiffFunction func(before, after string) []diff.Edit + +// Hooks contains configuration that is provided to the Gopls command by the +// main package. +type Hooks struct { + // LicensesText holds third party licenses for software used by gopls. + LicensesText string + + // GoDiff is used in gopls/hooks to get Myers' diff + GoDiff bool + + // Whether staticcheck is supported. + StaticcheckSupported bool + + // ComputeEdits is used to compute edits between file versions. + ComputeEdits DiffFunction + + // URLRegexp is used to find potential URLs in comments/strings. + // + // Not all matches are shown to the user: if the matched URL is not detected + // as valid, it will be skipped. + URLRegexp *regexp.Regexp + + // GofumptFormat allows the gopls module to wire-in a call to + // gofumpt/format.Source. langVersion and modulePath are used for some + // Gofumpt formatting rules -- see the Gofumpt documentation for details. + GofumptFormat func(ctx context.Context, langVersion, modulePath string, src []byte) ([]byte, error) + + DefaultAnalyzers map[string]*Analyzer + TypeErrorAnalyzers map[string]*Analyzer + ConvenienceAnalyzers map[string]*Analyzer + StaticcheckAnalyzers map[string]*Analyzer +} + +// InternalOptions contains settings that are not intended for use by the +// average user. These may be settings used by tests or outdated settings that +// will soon be deprecated. Some of these settings may not even be configurable +// by the user. +type InternalOptions struct { + // LiteralCompletions controls whether literal candidates such as + // "&someStruct{}" are offered. Tests disable this flag to simplify + // their expected values. + LiteralCompletions bool + + // VerboseWorkDoneProgress controls whether the LSP server should send + // progress reports for all work done outside the scope of an RPC. + // Used by the regression tests. + VerboseWorkDoneProgress bool + + // The following options were previously available to users, but they + // really shouldn't be configured by anyone other than "power users". + + // CompletionDocumentation enables documentation with completion results. + CompletionDocumentation bool + + // CompleteUnimported enables completion for packages that you do not + // currently import. + CompleteUnimported bool + + // DeepCompletion enables the ability to return completions from deep + // inside relevant entities, rather than just the locally accessible ones. + // + // Consider this example: + // + // ```go + // package main + // + // import "fmt" + // + // type wrapString struct { + // str string + // } + // + // func main() { + // x := wrapString{"hello world"} + // fmt.Printf(<>) + // } + // ``` + // + // At the location of the `<>` in this program, deep completion would suggest + // the result `x.str`. + DeepCompletion bool + + // TempModfile controls the use of the -modfile flag in Go 1.14. + TempModfile bool + + // ShowBugReports causes a message to be shown when the first bug is reported + // on the server. + // This option applies only during initialization. + ShowBugReports bool + + // NewDiff controls the choice of the new diff implementation. It can be + // 'new', 'old', or 'both', which is the default. 'both' computes diffs with + // both algorithms, checks that the new algorithm has worked, and write some + // summary statistics to a file in os.TmpDir(). + NewDiff string + + // ChattyDiagnostics controls whether to report file diagnostics for each + // file change. If unset, gopls only reports diagnostics when they change, or + // when a file is opened or closed. + ChattyDiagnostics bool +} + +type ImportShortcut string + +const ( + Both ImportShortcut = "Both" + Link ImportShortcut = "Link" + Definition ImportShortcut = "Definition" +) + +func (s ImportShortcut) ShowLinks() bool { + return s == Both || s == Link +} + +func (s ImportShortcut) ShowDefinition() bool { + return s == Both || s == Definition +} + +type Matcher string + +const ( + Fuzzy Matcher = "Fuzzy" + CaseInsensitive Matcher = "CaseInsensitive" + CaseSensitive Matcher = "CaseSensitive" +) + +type SymbolMatcher string + +const ( + SymbolFuzzy SymbolMatcher = "Fuzzy" + SymbolFastFuzzy SymbolMatcher = "FastFuzzy" + SymbolCaseInsensitive SymbolMatcher = "CaseInsensitive" + SymbolCaseSensitive SymbolMatcher = "CaseSensitive" +) + +type SymbolStyle string + +const ( + // PackageQualifiedSymbols is package qualified symbols i.e. + // "pkg.Foo.Field". + PackageQualifiedSymbols SymbolStyle = "Package" + // FullyQualifiedSymbols is fully qualified symbols, i.e. + // "path/to/pkg.Foo.Field". + FullyQualifiedSymbols SymbolStyle = "Full" + // DynamicSymbols uses whichever qualifier results in the highest scoring + // match for the given symbol query. Here a "qualifier" is any "/" or "." + // delimited suffix of the fully qualified symbol. i.e. "to/pkg.Foo.Field" or + // just "Foo.Field". + DynamicSymbols SymbolStyle = "Dynamic" +) + +type HoverKind string + +const ( + SingleLine HoverKind = "SingleLine" + NoDocumentation HoverKind = "NoDocumentation" + SynopsisDocumentation HoverKind = "SynopsisDocumentation" + FullDocumentation HoverKind = "FullDocumentation" + + // Structured is an experimental setting that returns a structured hover format. + // This format separates the signature from the documentation, so that the client + // can do more manipulation of these fields. + // + // This should only be used by clients that support this behavior. + Structured HoverKind = "Structured" +) + +type MemoryMode string + +const ( + ModeNormal MemoryMode = "Normal" + // In DegradeClosed mode, `gopls` will collect less information about + // packages without open files. As a result, features like Find + // References and Rename will miss results in such packages. + ModeDegradeClosed MemoryMode = "DegradeClosed" +) + +type OptionResults []OptionResult + +type OptionResult struct { + Name string + Value interface{} + Error error +} + +func SetOptions(options *Options, opts interface{}) OptionResults { + var results OptionResults + switch opts := opts.(type) { + case nil: + case map[string]interface{}: + // If the user's settings contains "allExperiments", set that first, + // and then let them override individual settings independently. + var enableExperiments bool + for name, value := range opts { + if b, ok := value.(bool); name == "allExperiments" && ok && b { + enableExperiments = true + options.EnableAllExperiments() + } + } + seen := map[string]struct{}{} + for name, value := range opts { + results = append(results, options.set(name, value, seen)) + } + // Finally, enable any experimental features that are specified in + // maps, which allows users to individually toggle them on or off. + if enableExperiments { + options.enableAllExperimentMaps() + } + default: + results = append(results, OptionResult{ + Value: opts, + Error: fmt.Errorf("Invalid options type %T", opts), + }) + } + return results +} + +func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) { + // Check if the client supports snippets in completion items. + if caps.Workspace.WorkspaceEdit != nil { + o.SupportedResourceOperations = caps.Workspace.WorkspaceEdit.ResourceOperations + } + if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport { + o.InsertTextFormat = protocol.SnippetTextFormat + } + // Check if the client supports configuration messages. + o.ConfigurationSupported = caps.Workspace.Configuration + o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration + o.DynamicRegistrationSemanticTokensSupported = caps.TextDocument.SemanticTokens.DynamicRegistration + o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration + + // Check which types of content format are supported by this client. + if hover := caps.TextDocument.Hover; len(hover.ContentFormat) > 0 { + o.PreferredContentFormat = hover.ContentFormat[0] + } + // Check if the client supports only line folding. + fr := caps.TextDocument.FoldingRange + o.LineFoldingOnly = fr.LineFoldingOnly + // Check if the client supports hierarchical document symbols. + o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport + // Check if the client supports semantic tokens + o.SemanticTypes = caps.TextDocument.SemanticTokens.TokenTypes + o.SemanticMods = caps.TextDocument.SemanticTokens.TokenModifiers + // we don't need Requests, as we support full functionality + // we don't need Formats, as there is only one, for now + + // Check if the client supports diagnostic related information. + o.RelatedInformationSupported = caps.TextDocument.PublishDiagnostics.RelatedInformation + // Check if the client completion support includes tags (preferred) or deprecation + if caps.TextDocument.Completion.CompletionItem.TagSupport.ValueSet != nil { + o.CompletionTags = true + } else if caps.TextDocument.Completion.CompletionItem.DeprecatedSupport { + o.CompletionDeprecated = true + } +} + +func (o *Options) Clone() *Options { + // TODO(rfindley): has this function gone stale? It appears that there are + // settings that are incorrectly cloned here (such as TemplateExtensions). + result := &Options{ + ClientOptions: o.ClientOptions, + InternalOptions: o.InternalOptions, + Hooks: Hooks{ + GoDiff: o.GoDiff, + StaticcheckSupported: o.StaticcheckSupported, + ComputeEdits: o.ComputeEdits, + GofumptFormat: o.GofumptFormat, + URLRegexp: o.URLRegexp, + }, + ServerOptions: o.ServerOptions, + UserOptions: o.UserOptions, + } + // Fully clone any slice or map fields. Only Hooks, ExperimentalOptions, + // and UserOptions can be modified. + copyStringMap := func(src map[string]bool) map[string]bool { + dst := make(map[string]bool) + for k, v := range src { + dst[k] = v + } + return dst + } + result.Analyses = copyStringMap(o.Analyses) + result.Codelenses = copyStringMap(o.Codelenses) + + copySlice := func(src []string) []string { + dst := make([]string, len(src)) + copy(dst, src) + return dst + } + result.SetEnvSlice(o.EnvSlice()) + result.BuildFlags = copySlice(o.BuildFlags) + result.DirectoryFilters = copySlice(o.DirectoryFilters) + result.StandaloneTags = copySlice(o.StandaloneTags) + + copyAnalyzerMap := func(src map[string]*Analyzer) map[string]*Analyzer { + dst := make(map[string]*Analyzer) + for k, v := range src { + dst[k] = v + } + return dst + } + result.DefaultAnalyzers = copyAnalyzerMap(o.DefaultAnalyzers) + result.TypeErrorAnalyzers = copyAnalyzerMap(o.TypeErrorAnalyzers) + result.ConvenienceAnalyzers = copyAnalyzerMap(o.ConvenienceAnalyzers) + result.StaticcheckAnalyzers = copyAnalyzerMap(o.StaticcheckAnalyzers) + return result +} + +func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer, enabled bool, severity protocol.DiagnosticSeverity) { + o.StaticcheckAnalyzers[a.Name] = &Analyzer{ + Analyzer: a, + Enabled: enabled, + Severity: severity, + } +} + +// EnableAllExperiments turns on all of the experimental "off-by-default" +// features offered by gopls. Any experimental features specified in maps +// should be enabled in enableAllExperimentMaps. +func (o *Options) EnableAllExperiments() { + o.SemanticTokens = true +} + +func (o *Options) enableAllExperimentMaps() { + if _, ok := o.Codelenses[string(command.GCDetails)]; !ok { + o.Codelenses[string(command.GCDetails)] = true + } + if _, ok := o.Analyses[unusedparams.Analyzer.Name]; !ok { + o.Analyses[unusedparams.Analyzer.Name] = true + } + if _, ok := o.Analyses[unusedvariable.Analyzer.Name]; !ok { + o.Analyses[unusedvariable.Analyzer.Name] = true + } +} + +// validateDirectoryFilter validates if the filter string +// - is not empty +// - start with either + or - +// - doesn't contain currently unsupported glob operators: *, ? +func validateDirectoryFilter(ifilter string) (string, error) { + filter := fmt.Sprint(ifilter) + if filter == "" || (filter[0] != '+' && filter[0] != '-') { + return "", fmt.Errorf("invalid filter %v, must start with + or -", filter) + } + segs := strings.Split(filter[1:], "/") + unsupportedOps := [...]string{"?", "*"} + for _, seg := range segs { + if seg != "**" { + for _, op := range unsupportedOps { + if strings.Contains(seg, op) { + return "", fmt.Errorf("invalid filter %v, operator %v not supported. If you want to have this operator supported, consider filing an issue.", filter, op) + } + } + } + } + + return strings.TrimRight(filepath.FromSlash(filter), "/"), nil +} + +func (o *Options) set(name string, value interface{}, seen map[string]struct{}) OptionResult { + // Flatten the name in case we get options with a hierarchy. + split := strings.Split(name, ".") + name = split[len(split)-1] + + result := OptionResult{Name: name, Value: value} + if _, ok := seen[name]; ok { + result.parseErrorf("duplicate configuration for %s", name) + } + seen[name] = struct{}{} + + switch name { + case "env": + menv, ok := value.(map[string]interface{}) + if !ok { + result.parseErrorf("invalid type %T, expect map", value) + break + } + if o.Env == nil { + o.Env = make(map[string]string) + } + for k, v := range menv { + o.Env[k] = fmt.Sprint(v) + } + + case "buildFlags": + // TODO(rfindley): use asStringSlice. + iflags, ok := value.([]interface{}) + if !ok { + result.parseErrorf("invalid type %T, expect list", value) + break + } + flags := make([]string, 0, len(iflags)) + for _, flag := range iflags { + flags = append(flags, fmt.Sprintf("%s", flag)) + } + o.BuildFlags = flags + + case "directoryFilters": + // TODO(rfindley): use asStringSlice. + ifilters, ok := value.([]interface{}) + if !ok { + result.parseErrorf("invalid type %T, expect list", value) + break + } + var filters []string + for _, ifilter := range ifilters { + filter, err := validateDirectoryFilter(fmt.Sprintf("%v", ifilter)) + if err != nil { + result.parseErrorf("%v", err) + return result + } + filters = append(filters, strings.TrimRight(filepath.FromSlash(filter), "/")) + } + o.DirectoryFilters = filters + + case "memoryMode": + if s, ok := result.asOneOf( + string(ModeNormal), + string(ModeDegradeClosed), + ); ok { + o.MemoryMode = MemoryMode(s) + } + case "completionDocumentation": + result.setBool(&o.CompletionDocumentation) + case "usePlaceholders": + result.setBool(&o.UsePlaceholders) + case "deepCompletion": + result.setBool(&o.DeepCompletion) + case "completeUnimported": + result.setBool(&o.CompleteUnimported) + case "completionBudget": + result.setDuration(&o.CompletionBudget) + case "matcher": + if s, ok := result.asOneOf( + string(Fuzzy), + string(CaseSensitive), + string(CaseInsensitive), + ); ok { + o.Matcher = Matcher(s) + } + + case "symbolMatcher": + if s, ok := result.asOneOf( + string(SymbolFuzzy), + string(SymbolFastFuzzy), + string(SymbolCaseInsensitive), + string(SymbolCaseSensitive), + ); ok { + o.SymbolMatcher = SymbolMatcher(s) + } + + case "symbolStyle": + if s, ok := result.asOneOf( + string(FullyQualifiedSymbols), + string(PackageQualifiedSymbols), + string(DynamicSymbols), + ); ok { + o.SymbolStyle = SymbolStyle(s) + } + + case "hoverKind": + if s, ok := result.asOneOf( + string(NoDocumentation), + string(SingleLine), + string(SynopsisDocumentation), + string(FullDocumentation), + string(Structured), + ); ok { + o.HoverKind = HoverKind(s) + } + + case "linkTarget": + result.setString(&o.LinkTarget) + + case "linksInHover": + result.setBool(&o.LinksInHover) + + case "importShortcut": + if s, ok := result.asOneOf(string(Both), string(Link), string(Definition)); ok { + o.ImportShortcut = ImportShortcut(s) + } + + case "analyses": + result.setBoolMap(&o.Analyses) + + case "hints": + result.setBoolMap(&o.Hints) + + case "annotations": + result.setAnnotationMap(&o.Annotations) + + case "codelenses", "codelens": + var lensOverrides map[string]bool + result.setBoolMap(&lensOverrides) + if result.Error == nil { + if o.Codelenses == nil { + o.Codelenses = make(map[string]bool) + } + for lens, enabled := range lensOverrides { + o.Codelenses[lens] = enabled + } + } + + // codelens is deprecated, but still works for now. + // TODO(rstambler): Remove this for the gopls/v0.7.0 release. + if name == "codelens" { + result.deprecated("codelenses") + } + + case "staticcheck": + if v, ok := result.asBool(); ok { + o.Staticcheck = v + if v && !o.StaticcheckSupported { + result.Error = fmt.Errorf("applying setting %q: staticcheck is not supported at %s;"+ + " rebuild gopls with a more recent version of Go", result.Name, runtime.Version()) + } + } + + case "local": + result.setString(&o.Local) + + case "verboseOutput": + result.setBool(&o.VerboseOutput) + + case "verboseWorkDoneProgress": + result.setBool(&o.VerboseWorkDoneProgress) + + case "tempModfile": + result.setBool(&o.TempModfile) + + case "showBugReports": + result.setBool(&o.ShowBugReports) + + case "gofumpt": + if v, ok := result.asBool(); ok { + o.Gofumpt = v + if v && o.GofumptFormat == nil { + result.Error = fmt.Errorf("applying setting %q: gofumpt is not supported at %s;"+ + " rebuild gopls with a more recent version of Go", result.Name, runtime.Version()) + } + } + + case "semanticTokens": + result.setBool(&o.SemanticTokens) + + case "noSemanticString": + result.setBool(&o.NoSemanticString) + + case "noSemanticNumber": + result.setBool(&o.NoSemanticNumber) + + case "expandWorkspaceToModule": + result.setBool(&o.ExpandWorkspaceToModule) + + case "experimentalPostfixCompletions": + result.setBool(&o.ExperimentalPostfixCompletions) + + case "experimentalWorkspaceModule": + const msg = "experimentalWorkspaceModule has been replaced by go workspaces, " + + "and will be removed in a future version of gopls (https://go.dev/issue/55331) -- " + + "see https://github.com/golang/tools/blob/master/gopls/doc/workspace.md " + + "for information on setting up multi-module workspaces using go.work files" + result.softErrorf(msg) + result.setBool(&o.ExperimentalWorkspaceModule) + + case "experimentalTemplateSupport": // TODO(pjw): remove after June 2022 + result.deprecated("") + + case "templateExtensions": + if iexts, ok := value.([]interface{}); ok { + ans := []string{} + for _, x := range iexts { + ans = append(ans, fmt.Sprint(x)) + } + o.TemplateExtensions = ans + break + } + if value == nil { + o.TemplateExtensions = nil + break + } + result.parseErrorf("unexpected type %T not []string", value) + + case "experimentalDiagnosticsDelay": + result.deprecated("diagnosticsDelay") + + case "diagnosticsDelay": + result.setDuration(&o.DiagnosticsDelay) + + case "experimentalWatchedFileDelay": + const msg = "experimentalWatchedFileDelay is deprecated, and will " + + "be removed in a future version of gopls (https://go.dev/issue/55332)" + result.softErrorf(msg) + result.setDuration(&o.ExperimentalWatchedFileDelay) + + case "experimentalPackageCacheKey": + result.setBool(&o.ExperimentalPackageCacheKey) + + case "allowModfileModifications": + result.setBool(&o.AllowModfileModifications) + + case "allowImplicitNetworkAccess": + result.setBool(&o.AllowImplicitNetworkAccess) + + case "experimentalUseInvalidMetadata": + const msg = "experimentalUseInvalidMetadata is deprecated, and will be removed " + + "in a future version of gopls (https://go.dev/issue/55333)" + result.softErrorf(msg) + result.setBool(&o.ExperimentalUseInvalidMetadata) + + case "standaloneTags": + result.setStringSlice(&o.StandaloneTags) + + case "allExperiments": + // This setting should be handled before all of the other options are + // processed, so do nothing here. + + case "newDiff": + result.setString(&o.NewDiff) + + case "chattyDiagnostics": + result.setBool(&o.ChattyDiagnostics) + + // Replaced settings. + case "experimentalDisabledAnalyses": + result.deprecated("analyses") + + case "disableDeepCompletion": + result.deprecated("deepCompletion") + + case "disableFuzzyMatching": + result.deprecated("fuzzyMatching") + + case "wantCompletionDocumentation": + result.deprecated("completionDocumentation") + + case "wantUnimportedCompletions": + result.deprecated("completeUnimported") + + case "fuzzyMatching": + result.deprecated("matcher") + + case "caseSensitiveCompletion": + result.deprecated("matcher") + + // Deprecated settings. + case "wantSuggestedFixes": + result.deprecated("") + + case "noIncrementalSync": + result.deprecated("") + + case "watchFileChanges": + result.deprecated("") + + case "go-diff": + result.deprecated("") + + default: + result.unexpected() + } + return result +} + +// parseErrorf reports an error parsing the current configuration value. +func (r *OptionResult) parseErrorf(msg string, values ...interface{}) { + if false { + _ = fmt.Sprintf(msg, values...) // this causes vet to check this like printf + } + prefix := fmt.Sprintf("parsing setting %q: ", r.Name) + r.Error = fmt.Errorf(prefix+msg, values...) +} + +// A SoftError is an error that does not affect the functionality of gopls. +type SoftError struct { + msg string +} + +func (e *SoftError) Error() string { + return e.msg +} + +// softErrorf reports an error that does not affect the functionality of gopls +// (a warning in the UI). +// The formatted message will be shown to the user unmodified. +func (r *OptionResult) softErrorf(format string, values ...interface{}) { + msg := fmt.Sprintf(format, values...) + r.Error = &SoftError{msg} +} + +// deprecated reports the current setting as deprecated. If 'replacement' is +// non-nil, it is suggested to the user. +func (r *OptionResult) deprecated(replacement string) { + msg := fmt.Sprintf("gopls setting %q is deprecated", r.Name) + if replacement != "" { + msg = fmt.Sprintf("%s, use %q instead", msg, replacement) + } + r.Error = &SoftError{msg} +} + +// unexpected reports that the current setting is not known to gopls. +func (r *OptionResult) unexpected() { + r.Error = fmt.Errorf("unexpected gopls setting %q", r.Name) +} + +func (r *OptionResult) asBool() (bool, bool) { + b, ok := r.Value.(bool) + if !ok { + r.parseErrorf("invalid type %T, expect bool", r.Value) + return false, false + } + return b, true +} + +func (r *OptionResult) setBool(b *bool) { + if v, ok := r.asBool(); ok { + *b = v + } +} + +func (r *OptionResult) setDuration(d *time.Duration) { + if v, ok := r.asString(); ok { + parsed, err := time.ParseDuration(v) + if err != nil { + r.parseErrorf("failed to parse duration %q: %v", v, err) + return + } + *d = parsed + } +} + +func (r *OptionResult) setBoolMap(bm *map[string]bool) { + m := r.asBoolMap() + *bm = m +} + +func (r *OptionResult) setAnnotationMap(bm *map[Annotation]bool) { + all := r.asBoolMap() + if all == nil { + return + } + // Default to everything enabled by default. + m := make(map[Annotation]bool) + for k, enabled := range all { + a, err := asOneOf( + k, + string(Nil), + string(Escape), + string(Inline), + string(Bounds), + ) + if err != nil { + // In case of an error, process any legacy values. + switch k { + case "noEscape": + m[Escape] = false + r.parseErrorf(`"noEscape" is deprecated, set "Escape: false" instead`) + case "noNilcheck": + m[Nil] = false + r.parseErrorf(`"noNilcheck" is deprecated, set "Nil: false" instead`) + case "noInline": + m[Inline] = false + r.parseErrorf(`"noInline" is deprecated, set "Inline: false" instead`) + case "noBounds": + m[Bounds] = false + r.parseErrorf(`"noBounds" is deprecated, set "Bounds: false" instead`) + default: + r.parseErrorf("%v", err) + } + continue + } + m[Annotation(a)] = enabled + } + *bm = m +} + +func (r *OptionResult) asBoolMap() map[string]bool { + all, ok := r.Value.(map[string]interface{}) + if !ok { + r.parseErrorf("invalid type %T for map[string]bool option", r.Value) + return nil + } + m := make(map[string]bool) + for a, enabled := range all { + if e, ok := enabled.(bool); ok { + m[a] = e + } else { + r.parseErrorf("invalid type %T for map key %q", enabled, a) + return m + } + } + return m +} + +func (r *OptionResult) asString() (string, bool) { + b, ok := r.Value.(string) + if !ok { + r.parseErrorf("invalid type %T, expect string", r.Value) + return "", false + } + return b, true +} + +func (r *OptionResult) asStringSlice() ([]string, bool) { + iList, ok := r.Value.([]interface{}) + if !ok { + r.parseErrorf("invalid type %T, expect list", r.Value) + return nil, false + } + var list []string + for _, elem := range iList { + s, ok := elem.(string) + if !ok { + r.parseErrorf("invalid element type %T, expect string", elem) + return nil, false + } + list = append(list, s) + } + return list, true +} + +func (r *OptionResult) asOneOf(options ...string) (string, bool) { + s, ok := r.asString() + if !ok { + return "", false + } + s, err := asOneOf(s, options...) + if err != nil { + r.parseErrorf("%v", err) + } + return s, err == nil +} + +func asOneOf(str string, options ...string) (string, error) { + lower := strings.ToLower(str) + for _, opt := range options { + if strings.ToLower(opt) == lower { + return opt, nil + } + } + return "", fmt.Errorf("invalid option %q for enum", str) +} + +func (r *OptionResult) setString(s *string) { + if v, ok := r.asString(); ok { + *s = v + } +} + +func (r *OptionResult) setStringSlice(s *[]string) { + if v, ok := r.asStringSlice(); ok { + *s = v + } +} + +// EnabledAnalyzers returns all of the analyzers enabled for the given +// snapshot. +func EnabledAnalyzers(snapshot Snapshot) (analyzers []*Analyzer) { + for _, a := range snapshot.View().Options().DefaultAnalyzers { + if a.IsEnabled(snapshot.View()) { + analyzers = append(analyzers, a) + } + } + for _, a := range snapshot.View().Options().TypeErrorAnalyzers { + if a.IsEnabled(snapshot.View()) { + analyzers = append(analyzers, a) + } + } + for _, a := range snapshot.View().Options().ConvenienceAnalyzers { + if a.IsEnabled(snapshot.View()) { + analyzers = append(analyzers, a) + } + } + for _, a := range snapshot.View().Options().StaticcheckAnalyzers { + if a.IsEnabled(snapshot.View()) { + analyzers = append(analyzers, a) + } + } + return analyzers +} + +func typeErrorAnalyzers() map[string]*Analyzer { + return map[string]*Analyzer{ + fillreturns.Analyzer.Name: { + Analyzer: fillreturns.Analyzer, + ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, + Enabled: true, + }, + nonewvars.Analyzer.Name: { + Analyzer: nonewvars.Analyzer, + Enabled: true, + }, + noresultvalues.Analyzer.Name: { + Analyzer: noresultvalues.Analyzer, + Enabled: true, + }, + undeclaredname.Analyzer.Name: { + Analyzer: undeclaredname.Analyzer, + Fix: UndeclaredName, + Enabled: true, + }, + unusedvariable.Analyzer.Name: { + Analyzer: unusedvariable.Analyzer, + Enabled: false, + }, + } +} + +func convenienceAnalyzers() map[string]*Analyzer { + return map[string]*Analyzer{ + fillstruct.Analyzer.Name: { + Analyzer: fillstruct.Analyzer, + Fix: FillStruct, + Enabled: true, + ActionKind: []protocol.CodeActionKind{protocol.RefactorRewrite}, + }, + stubmethods.Analyzer.Name: { + Analyzer: stubmethods.Analyzer, + ActionKind: []protocol.CodeActionKind{protocol.RefactorRewrite}, + Fix: StubMethods, + Enabled: true, + }, + } +} + +func defaultAnalyzers() map[string]*Analyzer { + return map[string]*Analyzer{ + // The traditional vet suite: + asmdecl.Analyzer.Name: {Analyzer: asmdecl.Analyzer, Enabled: true}, + assign.Analyzer.Name: {Analyzer: assign.Analyzer, Enabled: true}, + atomic.Analyzer.Name: {Analyzer: atomic.Analyzer, Enabled: true}, + bools.Analyzer.Name: {Analyzer: bools.Analyzer, Enabled: true}, + buildtag.Analyzer.Name: {Analyzer: buildtag.Analyzer, Enabled: true}, + cgocall.Analyzer.Name: {Analyzer: cgocall.Analyzer, Enabled: true}, + composite.Analyzer.Name: {Analyzer: composite.Analyzer, Enabled: true}, + copylock.Analyzer.Name: {Analyzer: copylock.Analyzer, Enabled: true}, + errorsas.Analyzer.Name: {Analyzer: errorsas.Analyzer, Enabled: true}, + httpresponse.Analyzer.Name: {Analyzer: httpresponse.Analyzer, Enabled: true}, + ifaceassert.Analyzer.Name: {Analyzer: ifaceassert.Analyzer, Enabled: true}, + loopclosure.Analyzer.Name: {Analyzer: loopclosure.Analyzer, Enabled: true}, + lostcancel.Analyzer.Name: {Analyzer: lostcancel.Analyzer, Enabled: true}, + nilfunc.Analyzer.Name: {Analyzer: nilfunc.Analyzer, Enabled: true}, + printf.Analyzer.Name: {Analyzer: printf.Analyzer, Enabled: true}, + shift.Analyzer.Name: {Analyzer: shift.Analyzer, Enabled: true}, + stdmethods.Analyzer.Name: {Analyzer: stdmethods.Analyzer, Enabled: true}, + stringintconv.Analyzer.Name: {Analyzer: stringintconv.Analyzer, Enabled: true}, + structtag.Analyzer.Name: {Analyzer: structtag.Analyzer, Enabled: true}, + tests.Analyzer.Name: {Analyzer: tests.Analyzer, Enabled: true}, + unmarshal.Analyzer.Name: {Analyzer: unmarshal.Analyzer, Enabled: true}, + unreachable.Analyzer.Name: {Analyzer: unreachable.Analyzer, Enabled: true}, + unsafeptr.Analyzer.Name: {Analyzer: unsafeptr.Analyzer, Enabled: true}, + unusedresult.Analyzer.Name: {Analyzer: unusedresult.Analyzer, Enabled: true}, + + // Non-vet analyzers: + atomicalign.Analyzer.Name: {Analyzer: atomicalign.Analyzer, Enabled: true}, + deepequalerrors.Analyzer.Name: {Analyzer: deepequalerrors.Analyzer, Enabled: true}, + fieldalignment.Analyzer.Name: {Analyzer: fieldalignment.Analyzer, Enabled: false}, + nilness.Analyzer.Name: {Analyzer: nilness.Analyzer, Enabled: false}, + shadow.Analyzer.Name: {Analyzer: shadow.Analyzer, Enabled: false}, + sortslice.Analyzer.Name: {Analyzer: sortslice.Analyzer, Enabled: true}, + testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, Enabled: true}, + unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, Enabled: false}, + unusedwrite.Analyzer.Name: {Analyzer: unusedwrite.Analyzer, Enabled: false}, + useany.Analyzer.Name: {Analyzer: useany.Analyzer, Enabled: false}, + infertypeargs.Analyzer.Name: {Analyzer: infertypeargs.Analyzer, Enabled: true}, + embeddirective.Analyzer.Name: {Analyzer: embeddirective.Analyzer, Enabled: true}, + timeformat.Analyzer.Name: {Analyzer: timeformat.Analyzer, Enabled: true}, + + // gofmt -s suite: + simplifycompositelit.Analyzer.Name: { + Analyzer: simplifycompositelit.Analyzer, + Enabled: true, + ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, + }, + simplifyrange.Analyzer.Name: { + Analyzer: simplifyrange.Analyzer, + Enabled: true, + ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, + }, + simplifyslice.Analyzer.Name: { + Analyzer: simplifyslice.Analyzer, + Enabled: true, + ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, + }, + } +} + +func urlRegexp() *regexp.Regexp { + // Ensure links are matched as full words, not anywhere. + re := regexp.MustCompile(`\b(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?\b`) + re.Longest() + return re +} + +type APIJSON struct { + Options map[string][]*OptionJSON + Commands []*CommandJSON + Lenses []*LensJSON + Analyzers []*AnalyzerJSON + Hints []*HintJSON +} + +type OptionJSON struct { + Name string + Type string + Doc string + EnumKeys EnumKeys + EnumValues []EnumValue + Default string + Status string + Hierarchy string +} + +func (o *OptionJSON) String() string { + return o.Name +} + +func (o *OptionJSON) Write(w io.Writer) { + fmt.Fprintf(w, "**%v** *%v*\n\n", o.Name, o.Type) + writeStatus(w, o.Status) + enumValues := collectEnums(o) + fmt.Fprintf(w, "%v%v\nDefault: `%v`.\n\n", o.Doc, enumValues, o.Default) +} + +func writeStatus(section io.Writer, status string) { + switch status { + case "": + case "advanced": + fmt.Fprint(section, "**This is an advanced setting and should not be configured by most `gopls` users.**\n\n") + case "debug": + fmt.Fprint(section, "**This setting is for debugging purposes only.**\n\n") + case "experimental": + fmt.Fprint(section, "**This setting is experimental and may be deleted.**\n\n") + default: + fmt.Fprintf(section, "**Status: %s.**\n\n", status) + } +} + +var parBreakRE = regexp.MustCompile("\n{2,}") + +func collectEnums(opt *OptionJSON) string { + var b strings.Builder + write := func(name, doc string, index, len int) { + if doc != "" { + unbroken := parBreakRE.ReplaceAllString(doc, "\\\n") + fmt.Fprintf(&b, "* %s\n", strings.TrimSpace(unbroken)) + } else { + fmt.Fprintf(&b, "* `%s`\n", name) + } + } + if len(opt.EnumValues) > 0 && opt.Type == "enum" { + b.WriteString("\nMust be one of:\n\n") + for i, val := range opt.EnumValues { + write(val.Value, val.Doc, i, len(opt.EnumValues)) + } + } else if len(opt.EnumKeys.Keys) > 0 && shouldShowEnumKeysInSettings(opt.Name) { + b.WriteString("\nCan contain any of:\n\n") + for i, val := range opt.EnumKeys.Keys { + write(val.Name, val.Doc, i, len(opt.EnumKeys.Keys)) + } + } + return b.String() +} + +func shouldShowEnumKeysInSettings(name string) bool { + // These fields have too many possible options to print. + return !(name == "analyses" || name == "codelenses" || name == "hints") +} + +type EnumKeys struct { + ValueType string + Keys []EnumKey +} + +type EnumKey struct { + Name string + Doc string + Default string +} + +type EnumValue struct { + Value string + Doc string +} + +type CommandJSON struct { + Command string + Title string + Doc string + ArgDoc string + ResultDoc string +} + +func (c *CommandJSON) String() string { + return c.Command +} + +func (c *CommandJSON) Write(w io.Writer) { + fmt.Fprintf(w, "### **%v**\nIdentifier: `%v`\n\n%v\n\n", c.Title, c.Command, c.Doc) + if c.ArgDoc != "" { + fmt.Fprintf(w, "Args:\n\n```\n%s\n```\n\n", c.ArgDoc) + } + if c.ResultDoc != "" { + fmt.Fprintf(w, "Result:\n\n```\n%s\n```\n\n", c.ResultDoc) + } +} + +type LensJSON struct { + Lens string + Title string + Doc string +} + +func (l *LensJSON) String() string { + return l.Title +} + +func (l *LensJSON) Write(w io.Writer) { + fmt.Fprintf(w, "%s (%s): %s", l.Title, l.Lens, l.Doc) +} + +type AnalyzerJSON struct { + Name string + Doc string + Default bool +} + +func (a *AnalyzerJSON) String() string { + return a.Name +} + +func (a *AnalyzerJSON) Write(w io.Writer) { + fmt.Fprintf(w, "%s (%s): %v", a.Name, a.Doc, a.Default) +} + +type HintJSON struct { + Name string + Doc string + Default bool +} + +func (h *HintJSON) String() string { + return h.Name +} + +func (h *HintJSON) Write(w io.Writer) { + fmt.Fprintf(w, "%s (%s): %v", h.Name, h.Doc, h.Default) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/references.go b/gopls/golang_org_x_tools_gopls/lsp/source/references.go new file mode 100644 index 0000000..9fe4864 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/references.go @@ -0,0 +1,280 @@ +// 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 source + +import ( + "context" + "errors" + "fmt" + "go/ast" + + "go/token" + "go/types" + "sort" + "strconv" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/bug" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +// ReferenceInfo holds information about reference to an identifier in Go source. +type ReferenceInfo struct { + Name string + MappedRange + ident *ast.Ident + obj types.Object + pkg Package + isDeclaration bool +} + +// isInPackageName reports whether the file's package name surrounds the +// given position pp (e.g. "foo" surrounds the cursor in "package foo"). +func isInPackageName(ctx context.Context, s Snapshot, f FileHandle, pgf *ParsedGoFile, pp protocol.Position) (bool, error) { + // Find position of the package name declaration + cursorPos, err := pgf.Mapper.Pos(pp) + if err != nil { + return false, err + } + + return pgf.File.Name.Pos() <= cursorPos && cursorPos <= pgf.File.Name.End(), nil +} + +// References returns a list of references for a given identifier within the packages +// containing i.File. Declarations appear first in the result. +func References(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position, includeDeclaration bool) ([]*ReferenceInfo, error) { + ctx, done := event.Start(ctx, "source.References") + defer done() + + // Find position of the package name declaration + pgf, err := s.ParseGo(ctx, f, ParseFull) + if err != nil { + return nil, err + } + + packageName := pgf.File.Name.Name // from package decl + inPackageName, err := isInPackageName(ctx, s, f, pgf, pp) + if err != nil { + return nil, err + } + + if inPackageName { + // TODO(rfindley): this is inaccurate, excluding test variants, and + // redundant with package renaming. Refactor to share logic. + renamingPkg, err := s.PackageForFile(ctx, f.URI(), TypecheckWorkspace, NarrowestPackage) + if err != nil { + return nil, err + } + + // Find external references to the package. + rdeps, err := s.GetReverseDependencies(ctx, renamingPkg.ID()) + if err != nil { + return nil, err + } + var refs []*ReferenceInfo + for _, dep := range rdeps { + for _, f := range dep.CompiledGoFiles() { + for _, imp := range f.File.Imports { + if path, err := strconv.Unquote(imp.Path.Value); err == nil && path == string(renamingPkg.PkgPath()) { + refs = append(refs, &ReferenceInfo{ + Name: packageName, + MappedRange: NewMappedRange(f.Tok, f.Mapper, imp.Pos(), imp.End()), + }) + } + } + } + } + + // Find internal references to the package within the package itself + for _, f := range renamingPkg.CompiledGoFiles() { + refs = append(refs, &ReferenceInfo{ + Name: packageName, + MappedRange: NewMappedRange(f.Tok, f.Mapper, f.File.Name.Pos(), f.File.Name.End()), + }) + } + + return refs, nil + } + + qualifiedObjs, err := qualifiedObjsAtProtocolPos(ctx, s, f.URI(), pp) + // Don't return references for builtin types. + if errors.Is(err, errBuiltin) { + return nil, nil + } + if err != nil { + return nil, err + } + + refs, err := references(ctx, s, qualifiedObjs, includeDeclaration, true, false) + if err != nil { + return nil, err + } + + toSort := refs + if includeDeclaration { + toSort = refs[1:] + } + sort.Slice(toSort, func(i, j int) bool { + x := CompareURI(toSort[i].URI(), toSort[j].URI()) + if x == 0 { + return toSort[i].ident.Pos() < toSort[j].ident.Pos() + } + return x < 0 + }) + return refs, nil +} + +// references is a helper function to avoid recomputing qualifiedObjsAtProtocolPos. +// The first element of qos is considered to be the declaration; +// if isDeclaration, the first result is an extra item for it. +// Only the definition-related fields of qualifiedObject are used. +// (Arguably it should accept a smaller data type.) +func references(ctx context.Context, snapshot Snapshot, qos []qualifiedObject, includeDeclaration, includeInterfaceRefs, includeEmbeddedRefs bool) ([]*ReferenceInfo, error) { + var ( + references []*ReferenceInfo + seen = make(map[positionKey]bool) + ) + + pos := qos[0].obj.Pos() + if pos == token.NoPos { + return nil, fmt.Errorf("no position for %s", qos[0].obj) // e.g. error.Error + } + // Inv: qos[0].pkg != nil, since Pos is valid. + // Inv: qos[*].pkg != nil, since all qos are logically the same declaration. + filename := qos[0].pkg.FileSet().File(pos).Name() + pgf, err := qos[0].pkg.File(span.URIFromPath(filename)) + if err != nil { + return nil, err + } + declIdent, err := findIdentifier(ctx, snapshot, qos[0].pkg, pgf, qos[0].obj.Pos()) + if err != nil { + return nil, err + } + // Make sure declaration is the first item in the response. + if includeDeclaration { + references = append(references, &ReferenceInfo{ + MappedRange: declIdent.MappedRange, + Name: qos[0].obj.Name(), + ident: declIdent.ident, + obj: qos[0].obj, + pkg: declIdent.pkg, + isDeclaration: true, + }) + } + + for _, qo := range qos { + var searchPkgs []Package + + // Only search dependents if the object is exported. + if qo.obj.Exported() { + reverseDeps, err := snapshot.GetReverseDependencies(ctx, qo.pkg.ID()) + if err != nil { + return nil, err + } + searchPkgs = append(searchPkgs, reverseDeps...) + } + // Add the package in which the identifier is declared. + searchPkgs = append(searchPkgs, qo.pkg) + for _, pkg := range searchPkgs { + for ident, obj := range pkg.GetTypesInfo().Uses { + // For instantiated objects (as in methods or fields on instantiated + // types), we may not have pointer-identical objects but still want to + // consider them references. + if !equalOrigin(obj, qo.obj) { + // If ident is not a use of qo.obj, skip it, with one exception: + // uses of an embedded field can be considered references of the + // embedded type name + if !includeEmbeddedRefs { + continue + } + v, ok := obj.(*types.Var) + if !ok || !v.Embedded() { + continue + } + named, ok := v.Type().(*types.Named) + if !ok || named.Obj() != qo.obj { + continue + } + } + key, found := packagePositionKey(pkg, ident.Pos()) + if !found { + bug.Reportf("ident %v (pos: %v) not found in package %v", ident.Name, ident.Pos(), pkg.Name()) + continue + } + if seen[key] { + continue + } + seen[key] = true + rng, err := posToMappedRange(pkg, ident.Pos(), ident.End()) + if err != nil { + return nil, err + } + references = append(references, &ReferenceInfo{ + Name: ident.Name, + ident: ident, + pkg: pkg, + obj: obj, + MappedRange: rng, + }) + } + } + } + + // When searching on type name, don't include interface references -- they + // would be things like all references to Stringer for any type that + // happened to have a String method. + _, isType := declIdent.Declaration.obj.(*types.TypeName) + if includeInterfaceRefs && !isType { + // TODO(adonovan): opt: don't go back into the position domain: + // we have complete type information already. + declRange, err := declIdent.Range() + if err != nil { + return nil, err + } + fh, err := snapshot.GetFile(ctx, declIdent.URI()) + if err != nil { + return nil, err + } + interfaceRefs, err := interfaceReferences(ctx, snapshot, fh, declRange.Start) + if err != nil { + return nil, err + } + references = append(references, interfaceRefs...) + } + + return references, nil +} + +// equalOrigin reports whether obj1 and obj2 have equivalent origin object. +// This may be the case even if obj1 != obj2, if one or both of them is +// instantiated. +func equalOrigin(obj1, obj2 types.Object) bool { + return obj1.Pkg() == obj2.Pkg() && obj1.Pos() == obj2.Pos() && obj1.Name() == obj2.Name() +} + +// interfaceReferences returns the references to the interfaces implemented by +// the type or method at the given position. +func interfaceReferences(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position) ([]*ReferenceInfo, error) { + implementations, err := implementations(ctx, s, f, pp) + if err != nil { + if errors.Is(err, ErrNotAType) { + return nil, nil + } + return nil, err + } + + // Make a separate call to references() for each element + // since it treats the first qualifiedObject as a definition. + var refs []*ReferenceInfo + for _, impl := range implementations { + implRefs, err := references(ctx, s, []qualifiedObject{impl}, false, false, false) + if err != nil { + return nil, err + } + refs = append(refs, implRefs...) + } + return refs, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/rename.go b/gopls/golang_org_x_tools_gopls/lsp/source/rename.go new file mode 100644 index 0000000..fb7e765 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/rename.go @@ -0,0 +1,714 @@ +// 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 source + +import ( + "context" + "errors" + "fmt" + "go/ast" + "go/token" + "go/types" + "path" + "regexp" + "sort" + "strconv" + "strings" + + "golang.org/x/tools/go/types/typeutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/diff" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "golang.org/x/tools/refactor/satisfy" +) + +type renamer struct { + ctx context.Context + refs []*ReferenceInfo + objsToUpdate map[types.Object]bool + hadConflicts bool + errors string + from, to string + satisfyConstraints map[satisfy.Constraint]bool + packages map[*types.Package]Package // may include additional packages that are a dep of pkg. + msets typeutil.MethodSetCache + changeMethods bool +} + +type PrepareItem struct { + Range protocol.Range + Text string +} + +// PrepareRename searches for a valid renaming at position pp. +// +// The returned usererr is intended to be displayed to the user to explain why +// the prepare fails. Probably we could eliminate the redundancy in returning +// two errors, but for now this is done defensively. +func PrepareRename(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position) (_ *PrepareItem, usererr, err error) { + // Find position of the package name declaration. + ctx, done := event.Start(ctx, "source.PrepareRename") + defer done() + pgf, err := snapshot.ParseGo(ctx, f, ParseFull) + if err != nil { + return nil, err, err + } + inPackageName, err := isInPackageName(ctx, snapshot, f, pgf, pp) + if err != nil { + return nil, err, err + } + + if inPackageName { + fileRenameSupported := false + for _, op := range snapshot.View().Options().SupportedResourceOperations { + if op == protocol.Rename { + fileRenameSupported = true + break + } + } + + if !fileRenameSupported { + err := errors.New("can't rename package: LSP client does not support file renaming") + return nil, err, err + } + fileMeta, err := snapshot.MetadataForFile(ctx, f.URI()) + if err != nil { + return nil, err, err + } + + if len(fileMeta) == 0 { + err := fmt.Errorf("no packages found for file %q", f.URI()) + return nil, err, err + } + + meta := fileMeta[0] + + if meta.PackageName() == "main" { + err := errors.New("can't rename package \"main\"") + return nil, err, err + } + + if strings.HasSuffix(string(meta.PackageName()), "_test") { + err := errors.New("can't rename x_test packages") + return nil, err, err + } + + if meta.ModuleInfo() == nil { + err := fmt.Errorf("can't rename package: missing module information for package %q", meta.PackagePath()) + return nil, err, err + } + + if meta.ModuleInfo().Path == string(meta.PackagePath()) { + err := fmt.Errorf("can't rename package: package path %q is the same as module path %q", meta.PackagePath(), meta.ModuleInfo().Path) + return nil, err, err + } + // TODO(rfindley): we should not need the package here. + pkg, err := snapshot.WorkspacePackageByID(ctx, meta.PackageID()) + if err != nil { + err = fmt.Errorf("error building package to rename: %v", err) + return nil, err, err + } + result, err := computePrepareRenameResp(snapshot, pkg, pgf.File.Name, string(pkg.Name())) + if err != nil { + return nil, nil, err + } + return result, nil, nil + } + + qos, err := qualifiedObjsAtProtocolPos(ctx, snapshot, f.URI(), pp) + if err != nil { + return nil, nil, err + } + node, obj, pkg := qos[0].node, qos[0].obj, qos[0].sourcePkg + if err := checkRenamable(obj); err != nil { + return nil, nil, err + } + result, err := computePrepareRenameResp(snapshot, pkg, node, obj.Name()) + if err != nil { + return nil, nil, err + } + return result, nil, nil +} + +func computePrepareRenameResp(snapshot Snapshot, pkg Package, node ast.Node, text string) (*PrepareItem, error) { + mr, err := posToMappedRange(pkg, node.Pos(), node.End()) + if err != nil { + return nil, err + } + rng, err := mr.Range() + if err != nil { + return nil, err + } + if _, isImport := node.(*ast.ImportSpec); isImport { + // We're not really renaming the import path. + rng.End = rng.Start + } + return &PrepareItem{ + Range: rng, + Text: text, + }, nil +} + +// checkRenamable verifies if an obj may be renamed. +func checkRenamable(obj types.Object) error { + if v, ok := obj.(*types.Var); ok && v.Embedded() { + return errors.New("can't rename embedded fields: rename the type directly or name the field") + } + if obj.Name() == "_" { + return errors.New("can't rename \"_\"") + } + return nil +} + +// Rename returns a map of TextEdits for each file modified when renaming a +// given identifier within a package and a boolean value of true for renaming +// package and false otherwise. +func Rename(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position, newName string) (map[span.URI][]protocol.TextEdit, bool, error) { + ctx, done := event.Start(ctx, "source.Rename") + defer done() + + pgf, err := s.ParseGo(ctx, f, ParseFull) + if err != nil { + return nil, false, err + } + inPackageName, err := isInPackageName(ctx, s, f, pgf, pp) + if err != nil { + return nil, false, err + } + + if inPackageName { + if !isValidIdentifier(newName) { + return nil, true, fmt.Errorf("%q is not a valid identifier", newName) + } + + fileMeta, err := s.MetadataForFile(ctx, f.URI()) + if err != nil { + return nil, true, err + } + + if len(fileMeta) == 0 { + return nil, true, fmt.Errorf("no packages found for file %q", f.URI()) + } + + // We need metadata for the relevant package and module paths. These should + // be the same for all packages containing the file. + // + // TODO(rfindley): we mix package path and import path here haphazardly. + // Fix this. + meta := fileMeta[0] + oldPath := meta.PackagePath() + var modulePath PackagePath + if mi := meta.ModuleInfo(); mi == nil { + return nil, true, fmt.Errorf("cannot rename package: missing module information for package %q", meta.PackagePath()) + } else { + modulePath = PackagePath(mi.Path) + } + + if strings.HasSuffix(newName, "_test") { + return nil, true, fmt.Errorf("cannot rename to _test package") + } + + metadata, err := s.AllValidMetadata(ctx) + if err != nil { + return nil, true, err + } + + renamingEdits, err := renamePackage(ctx, s, modulePath, oldPath, PackageName(newName), metadata) + if err != nil { + return nil, true, err + } + + return renamingEdits, true, nil + } + + qos, err := qualifiedObjsAtProtocolPos(ctx, s, f.URI(), pp) + if err != nil { + return nil, false, err + } + result, err := renameObj(ctx, s, newName, qos) + if err != nil { + return nil, false, err + } + + return result, false, nil +} + +// renamePackage computes all workspace edits required to rename the package +// described by the given metadata, to newName, by renaming its package +// directory. +// +// It updates package clauses and import paths for the renamed package as well +// as any other packages affected by the directory renaming among packages +// described by allMetadata. +func renamePackage(ctx context.Context, s Snapshot, modulePath, oldPath PackagePath, newName PackageName, allMetadata []Metadata) (map[span.URI][]protocol.TextEdit, error) { + if modulePath == oldPath { + return nil, fmt.Errorf("cannot rename package: module path %q is the same as the package path, so renaming the package directory would have no effect", modulePath) + } + + newPathPrefix := path.Join(path.Dir(string(oldPath)), string(newName)) + + edits := make(map[span.URI][]protocol.TextEdit) + seen := make(seenPackageRename) // track per-file import renaming we've already processed + + // Rename imports to the renamed package from other packages. + for _, m := range allMetadata { + // Special case: x_test packages for the renamed package will not have the + // package path as as a dir prefix, but still need their package clauses + // renamed. + if m.PackagePath() == oldPath+"_test" { + newTestName := newName + "_test" + + if err := renamePackageClause(ctx, m, s, newTestName, seen, edits); err != nil { + return nil, err + } + continue + } + + // Subtle: check this condition before checking for valid module info + // below, because we should not fail this operation if unrelated packages + // lack module info. + if !strings.HasPrefix(string(m.PackagePath())+"/", string(oldPath)+"/") { + continue // not affected by the package renaming + } + + if m.ModuleInfo() == nil { + return nil, fmt.Errorf("cannot rename package: missing module information for package %q", m.PackagePath()) + } + + if modulePath != PackagePath(m.ModuleInfo().Path) { + continue // don't edit imports if nested package and renaming package have different module paths + } + + // Renaming a package consists of changing its import path and package name. + suffix := strings.TrimPrefix(string(m.PackagePath()), string(oldPath)) + newPath := newPathPrefix + suffix + + pkgName := m.PackageName() + if m.PackagePath() == PackagePath(oldPath) { + pkgName = newName + + if err := renamePackageClause(ctx, m, s, newName, seen, edits); err != nil { + return nil, err + } + } + + imp := ImportPath(newPath) // TODO(adonovan): what if newPath has vendor/ prefix? + if err := renameImports(ctx, s, m, imp, pkgName, seen, edits); err != nil { + return nil, err + } + } + + return edits, nil +} + +// seenPackageRename tracks import path renamings that have already been +// processed. +// +// Due to test variants, files may appear multiple times in the reverse +// transitive closure of a renamed package, or in the reverse transitive +// closure of different variants of a renamed package (both are possible). +// However, in all cases the resulting edits will be the same. +type seenPackageRename map[seenPackageKey]bool +type seenPackageKey struct { + uri span.URI + path PackagePath +} + +// add reports whether uri and importPath have been seen, and records them as +// seen if not. +func (s seenPackageRename) add(uri span.URI, path PackagePath) bool { + key := seenPackageKey{uri, path} + seen := s[key] + if !seen { + s[key] = true + } + return seen +} + +// renamePackageClause computes edits renaming the package clause of files in +// the package described by the given metadata, to newName. +// +// As files may belong to multiple packages, the seen map tracks files whose +// package clause has already been updated, to prevent duplicate edits. +// +// Edits are written into the edits map. +func renamePackageClause(ctx context.Context, m Metadata, s Snapshot, newName PackageName, seen seenPackageRename, edits map[span.URI][]protocol.TextEdit) error { + pkg, err := s.WorkspacePackageByID(ctx, m.PackageID()) + if err != nil { + return err + } + + // Rename internal references to the package in the renaming package. + for _, f := range pkg.CompiledGoFiles() { + if seen.add(f.URI, m.PackagePath()) { + continue + } + + if f.File.Name == nil { + continue + } + pkgNameMappedRange := NewMappedRange(f.Tok, f.Mapper, f.File.Name.Pos(), f.File.Name.End()) + rng, err := pkgNameMappedRange.Range() + if err != nil { + return err + } + edits[f.URI] = append(edits[f.URI], protocol.TextEdit{ + Range: rng, + NewText: string(newName), + }) + } + + return nil +} + +// renameImports computes the set of edits to imports resulting from renaming +// the package described by the given metadata, to a package with import path +// newPath and name newName. +// +// Edits are written into the edits map. +func renameImports(ctx context.Context, s Snapshot, m Metadata, newPath ImportPath, newName PackageName, seen seenPackageRename, edits map[span.URI][]protocol.TextEdit) error { + // TODO(rfindley): we should get reverse dependencies as metadata first, + // rather then building the package immediately. We don't need reverse + // dependencies if they are intermediate test variants. + rdeps, err := s.GetReverseDependencies(ctx, m.PackageID()) + if err != nil { + return err + } + + for _, dep := range rdeps { + // Subtle: don't perform renaming in this package if it is not fully + // parsed. This can occur inside the workspace if dep is an intermediate + // test variant. ITVs are only ever parsed in export mode, and no file is + // found only in an ITV. Therefore the renaming will eventually occur in a + // full package. + // + // An alternative algorithm that may be more robust would be to first + // collect *files* that need to have their imports updated, and then + // perform the rename using s.PackageForFile(..., NarrowestPackage). + if dep.ParseMode() != ParseFull { + continue + } + + for _, f := range dep.CompiledGoFiles() { + if seen.add(f.URI, m.PackagePath()) { + continue + } + + for _, imp := range f.File.Imports { + // TODO(adonovan): what if RHS has "vendor/" prefix? + if UnquoteImportPath(imp) != ImportPath(m.PackagePath()) { + continue // not the import we're looking for + } + + // Create text edit for the import path (string literal). + impPathMappedRange := NewMappedRange(f.Tok, f.Mapper, imp.Path.Pos(), imp.Path.End()) + rng, err := impPathMappedRange.Range() + if err != nil { + return err + } + edits[f.URI] = append(edits[f.URI], protocol.TextEdit{ + Range: rng, + NewText: strconv.Quote(string(newPath)), + }) + + // If the package name of an import has not changed or if its import + // path already has a local package name, then we don't need to update + // the local package name. + if newName == m.PackageName() || imp.Name != nil { + continue + } + + // Rename the types.PkgName locally within this file. + pkgname := dep.GetTypesInfo().Implicits[imp].(*types.PkgName) + qos := []qualifiedObject{{obj: pkgname, pkg: dep}} + + pkgScope := dep.GetTypes().Scope() + fileScope := dep.GetTypesInfo().Scopes[f.File] + + var changes map[span.URI][]protocol.TextEdit + localName := string(newName) + try := 0 + + // Keep trying with fresh names until one succeeds. + for fileScope.Lookup(localName) != nil || pkgScope.Lookup(localName) != nil { + try++ + localName = fmt.Sprintf("%s%d", newName, try) + } + changes, err = renameObj(ctx, s, localName, qos) + if err != nil { + return err + } + + // If the chosen local package name matches the package's new name, delete the + // change that would have inserted an explicit local name, which is always + // the lexically first change. + if localName == string(newName) { + v := changes[f.URI] + sort.Slice(v, func(i, j int) bool { + return protocol.CompareRange(v[i].Range, v[j].Range) < 0 + }) + changes[f.URI] = v[1:] + } + for uri, changeEdits := range changes { + edits[uri] = append(edits[uri], changeEdits...) + } + } + } + } + + return nil +} + +// renameObj returns a map of TextEdits for renaming an identifier within a file +// and boolean value of true if there is no renaming conflicts and false otherwise. +func renameObj(ctx context.Context, s Snapshot, newName string, qos []qualifiedObject) (map[span.URI][]protocol.TextEdit, error) { + obj := qos[0].obj + + if err := checkRenamable(obj); err != nil { + return nil, err + } + if obj.Name() == newName { + return nil, fmt.Errorf("old and new names are the same: %s", newName) + } + if !isValidIdentifier(newName) { + return nil, fmt.Errorf("invalid identifier to rename: %q", newName) + } + refs, err := references(ctx, s, qos, true, false, true) + if err != nil { + return nil, err + } + r := renamer{ + ctx: ctx, + refs: refs, + objsToUpdate: make(map[types.Object]bool), + from: obj.Name(), + to: newName, + packages: make(map[*types.Package]Package), + } + + // A renaming initiated at an interface method indicates the + // intention to rename abstract and concrete methods as needed + // to preserve assignability. + for _, ref := range refs { + if obj, ok := ref.obj.(*types.Func); ok { + recv := obj.Type().(*types.Signature).Recv() + if recv != nil && IsInterface(recv.Type().Underlying()) { + r.changeMethods = true + break + } + } + } + for _, from := range refs { + r.packages[from.pkg.GetTypes()] = from.pkg + } + + // Check that the renaming of the identifier is ok. + for _, ref := range refs { + r.check(ref.obj) + if r.hadConflicts { // one error is enough. + break + } + } + if r.hadConflicts { + return nil, fmt.Errorf(r.errors) + } + + changes, err := r.update() + if err != nil { + return nil, err + } + + result := make(map[span.URI][]protocol.TextEdit) + for uri, edits := range changes { + // These edits should really be associated with FileHandles for maximal correctness. + // For now, this is good enough. + fh, err := s.GetFile(ctx, uri) + if err != nil { + return nil, err + } + data, err := fh.Read() + if err != nil { + return nil, err + } + m := protocol.NewColumnMapper(uri, data) + protocolEdits, err := ToProtocolEdits(m, edits) + if err != nil { + return nil, err + } + result[uri] = protocolEdits + } + return result, nil +} + +// Rename all references to the identifier. +func (r *renamer) update() (map[span.URI][]diff.Edit, error) { + result := make(map[span.URI][]diff.Edit) + seen := make(map[span.Span]bool) + + docRegexp, err := regexp.Compile(`\b` + r.from + `\b`) + if err != nil { + return nil, err + } + for _, ref := range r.refs { + refSpan, err := ref.Span() + if err != nil { + return nil, err + } + if seen[refSpan] { + continue + } + seen[refSpan] = true + + // Renaming a types.PkgName may result in the addition or removal of an identifier, + // so we deal with this separately. + if pkgName, ok := ref.obj.(*types.PkgName); ok && ref.isDeclaration { + edit, err := r.updatePkgName(pkgName) + if err != nil { + return nil, err + } + result[refSpan.URI()] = append(result[refSpan.URI()], *edit) + continue + } + + // Replace the identifier with r.to. + edit := diff.Edit{ + Start: refSpan.Start().Offset(), + End: refSpan.End().Offset(), + New: r.to, + } + + result[refSpan.URI()] = append(result[refSpan.URI()], edit) + + if !ref.isDeclaration || ref.ident == nil { // uses do not have doc comments to update. + continue + } + + doc := r.docComment(ref.pkg, ref.ident) + if doc == nil { + continue + } + + // Perform the rename in doc comments declared in the original package. + // go/parser strips out \r\n returns from the comment text, so go + // line-by-line through the comment text to get the correct positions. + for _, comment := range doc.List { + if isDirective(comment.Text) { + continue + } + // TODO(adonovan): why are we looping over lines? + // Just run the loop body once over the entire multiline comment. + lines := strings.Split(comment.Text, "\n") + tokFile := ref.pkg.FileSet().File(comment.Pos()) + commentLine := tokFile.Line(comment.Pos()) + uri := span.URIFromPath(tokFile.Name()) + for i, line := range lines { + lineStart := comment.Pos() + if i > 0 { + lineStart = tokFile.LineStart(commentLine + i) + } + for _, locs := range docRegexp.FindAllIndex([]byte(line), -1) { + // The File.Offset static check complains + // even though these uses are manifestly safe. + start, _ := safetoken.Offset(tokFile, lineStart+token.Pos(locs[0])) + end, _ := safetoken.Offset(tokFile, lineStart+token.Pos(locs[1])) + result[uri] = append(result[uri], diff.Edit{ + Start: start, + End: end, + New: r.to, + }) + } + } + } + } + + return result, nil +} + +// docComment returns the doc for an identifier. +func (r *renamer) docComment(pkg Package, id *ast.Ident) *ast.CommentGroup { + _, tokFile, nodes, _ := pathEnclosingInterval(pkg, id.Pos(), id.End()) + for _, node := range nodes { + switch decl := node.(type) { + case *ast.FuncDecl: + return decl.Doc + case *ast.Field: + return decl.Doc + case *ast.GenDecl: + return decl.Doc + // For {Type,Value}Spec, if the doc on the spec is absent, + // search for the enclosing GenDecl + case *ast.TypeSpec: + if decl.Doc != nil { + return decl.Doc + } + case *ast.ValueSpec: + if decl.Doc != nil { + return decl.Doc + } + case *ast.Ident: + case *ast.AssignStmt: + // *ast.AssignStmt doesn't have an associated comment group. + // So, we try to find a comment just before the identifier. + + // Try to find a comment group only for short variable declarations (:=). + if decl.Tok != token.DEFINE { + return nil + } + + identLine := tokFile.Line(id.Pos()) + for _, comment := range nodes[len(nodes)-1].(*ast.File).Comments { + if comment.Pos() > id.Pos() { + // Comment is after the identifier. + continue + } + + lastCommentLine := tokFile.Line(comment.End()) + if lastCommentLine+1 == identLine { + return comment + } + } + default: + return nil + } + } + return nil +} + +// updatePkgName returns the updates to rename a pkgName in the import spec by +// only modifying the package name portion of the import declaration. +func (r *renamer) updatePkgName(pkgName *types.PkgName) (*diff.Edit, error) { + // Modify ImportSpec syntax to add or remove the Name as needed. + pkg := r.packages[pkgName.Pkg()] + _, tokFile, path, _ := pathEnclosingInterval(pkg, pkgName.Pos(), pkgName.Pos()) + if len(path) < 2 { + return nil, fmt.Errorf("no path enclosing interval for %s", pkgName.Name()) + } + spec, ok := path[1].(*ast.ImportSpec) + if !ok { + return nil, fmt.Errorf("failed to update PkgName for %s", pkgName.Name()) + } + + newText := "" + if pkgName.Imported().Name() != r.to { + newText = r.to + " " + } + + // Replace the portion (possibly empty) of the spec before the path: + // local "path" or "path" + // -> <- -><- + rng := span.NewRange(tokFile, spec.Pos(), spec.Path.Pos()) + spn, err := rng.Span() + if err != nil { + return nil, err + } + + return &diff.Edit{ + Start: spn.Start().Offset(), + End: spn.End().Offset(), + New: newText, + }, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/rename_check.go b/gopls/golang_org_x_tools_gopls/lsp/source/rename_check.go new file mode 100644 index 0000000..ce625de --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/rename_check.go @@ -0,0 +1,935 @@ +// 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. +// +// Taken from golang.org/x/tools/refactor/rename. + +package source + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "reflect" + "strings" + "unicode" + + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/refactor/satisfy" +) + +// errorf reports an error (e.g. conflict) and prevents file modification. +func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) { + r.hadConflicts = true + r.errors += fmt.Sprintf(format, args...) +} + +// check performs safety checks of the renaming of the 'from' object to r.to. +func (r *renamer) check(from types.Object) { + if r.objsToUpdate[from] { + return + } + r.objsToUpdate[from] = true + + // NB: order of conditions is important. + if from_, ok := from.(*types.PkgName); ok { + r.checkInFileBlock(from_) + } else if from_, ok := from.(*types.Label); ok { + r.checkLabel(from_) + } else if isPackageLevel(from) { + r.checkInPackageBlock(from) + } else if v, ok := from.(*types.Var); ok && v.IsField() { + r.checkStructField(v) + } else if f, ok := from.(*types.Func); ok && recv(f) != nil { + r.checkMethod(f) + } else if isLocal(from) { + r.checkInLocalScope(from) + } else { + r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n", + objectKind(from), from) + } +} + +// checkInFileBlock performs safety checks for renames of objects in the file block, +// i.e. imported package names. +func (r *renamer) checkInFileBlock(from *types.PkgName) { + // Check import name is not "init". + if r.to == "init" { + r.errorf(from.Pos(), "%q is not a valid imported package name", r.to) + } + + // Check for conflicts between file and package block. + if prev := from.Pkg().Scope().Lookup(r.to); prev != nil { + r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", + objectKind(from), from.Name(), r.to) + r.errorf(prev.Pos(), "\twith this package member %s", + objectKind(prev)) + return // since checkInPackageBlock would report redundant errors + } + + // Check for conflicts in lexical scope. + r.checkInLexicalScope(from, r.packages[from.Pkg()]) +} + +// checkInPackageBlock performs safety checks for renames of +// func/var/const/type objects in the package block. +func (r *renamer) checkInPackageBlock(from types.Object) { + // Check that there are no references to the name from another + // package if the renaming would make it unexported. + if ast.IsExported(from.Name()) && !ast.IsExported(r.to) { + for typ, pkg := range r.packages { + if typ == from.Pkg() { + continue + } + if id := someUse(pkg.GetTypesInfo(), from); id != nil && + !r.checkExport(id, typ, from) { + break + } + } + } + + pkg := r.packages[from.Pkg()] + if pkg == nil { + return + } + + // Check that in the package block, "init" is a function, and never referenced. + if r.to == "init" { + kind := objectKind(from) + if kind == "func" { + // Reject if intra-package references to it exist. + for id, obj := range pkg.GetTypesInfo().Uses { + if obj == from { + r.errorf(from.Pos(), + "renaming this func %q to %q would make it a package initializer", + from.Name(), r.to) + r.errorf(id.Pos(), "\tbut references to it exist") + break + } + } + } else { + r.errorf(from.Pos(), "you cannot have a %s at package level named %q", + kind, r.to) + } + } + + // Check for conflicts between package block and all file blocks. + for _, f := range pkg.GetSyntax() { + fileScope := pkg.GetTypesInfo().Scopes[f] + b, prev := fileScope.LookupParent(r.to, token.NoPos) + if b == fileScope { + r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), r.to) + var prevPos token.Pos + if prev != nil { + prevPos = prev.Pos() + } + r.errorf(prevPos, "\twith this %s", objectKind(prev)) + return // since checkInPackageBlock would report redundant errors + } + } + + // Check for conflicts in lexical scope. + if from.Exported() { + for _, pkg := range r.packages { + r.checkInLexicalScope(from, pkg) + } + } else { + r.checkInLexicalScope(from, pkg) + } +} + +func (r *renamer) checkInLocalScope(from types.Object) { + pkg := r.packages[from.Pkg()] + r.checkInLexicalScope(from, pkg) +} + +// checkInLexicalScope performs safety checks that a renaming does not +// change the lexical reference structure of the specified package. +// +// For objects in lexical scope, there are three kinds of conflicts: +// same-, sub-, and super-block conflicts. We will illustrate all three +// using this example: +// +// var x int +// var z int +// +// func f(y int) { +// print(x) +// print(y) +// } +// +// Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object +// with the new name already exists, defined in the same lexical block +// as the old object. +// +// Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists +// a reference to x from within (what would become) a hole in its scope. +// The definition of y in an (inner) sub-block would cast a shadow in +// the scope of the renamed variable. +// +// Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the +// converse situation: there is an existing definition of the new name +// (x) in an (enclosing) super-block, and the renaming would create a +// hole in its scope, within which there exist references to it. The +// new name casts a shadow in scope of the existing definition of x in +// the super-block. +// +// Removing the old name (and all references to it) is always safe, and +// requires no checks. +func (r *renamer) checkInLexicalScope(from types.Object, pkg Package) { + b := from.Parent() // the block defining the 'from' object + if b != nil { + toBlock, to := b.LookupParent(r.to, from.Parent().End()) + if toBlock == b { + // same-block conflict + r.errorf(from.Pos(), "renaming this %s %q to %q", + objectKind(from), from.Name(), r.to) + r.errorf(to.Pos(), "\tconflicts with %s in same block", + objectKind(to)) + return + } else if toBlock != nil { + // Check for super-block conflict. + // The name r.to is defined in a superblock. + // Is that name referenced from within this block? + forEachLexicalRef(pkg, to, func(id *ast.Ident, block *types.Scope) bool { + _, obj := block.LookupParent(from.Name(), id.Pos()) + if obj == from { + // super-block conflict + r.errorf(from.Pos(), "renaming this %s %q to %q", + objectKind(from), from.Name(), r.to) + r.errorf(id.Pos(), "\twould shadow this reference") + r.errorf(to.Pos(), "\tto the %s declared here", + objectKind(to)) + return false // stop + } + return true + }) + } + } + // Check for sub-block conflict. + // Is there an intervening definition of r.to between + // the block defining 'from' and some reference to it? + forEachLexicalRef(pkg, from, func(id *ast.Ident, block *types.Scope) bool { + // Find the block that defines the found reference. + // It may be an ancestor. + fromBlock, _ := block.LookupParent(from.Name(), id.Pos()) + // See what r.to would resolve to in the same scope. + toBlock, to := block.LookupParent(r.to, id.Pos()) + if to != nil { + // sub-block conflict + if deeper(toBlock, fromBlock) { + r.errorf(from.Pos(), "renaming this %s %q to %q", + objectKind(from), from.Name(), r.to) + r.errorf(id.Pos(), "\twould cause this reference to become shadowed") + r.errorf(to.Pos(), "\tby this intervening %s definition", + objectKind(to)) + return false // stop + } + } + return true + }) + + // Renaming a type that is used as an embedded field + // requires renaming the field too. e.g. + // type T int // if we rename this to U.. + // var s struct {T} + // print(s.T) // ...this must change too + if _, ok := from.(*types.TypeName); ok { + for id, obj := range pkg.GetTypesInfo().Uses { + if obj == from { + if field := pkg.GetTypesInfo().Defs[id]; field != nil { + r.check(field) + } + } + } + } +} + +// deeper reports whether block x is lexically deeper than y. +func deeper(x, y *types.Scope) bool { + if x == y || x == nil { + return false + } else if y == nil { + return true + } else { + return deeper(x.Parent(), y.Parent()) + } +} + +// forEachLexicalRef calls fn(id, block) for each identifier id in package +// pkg that is a reference to obj in lexical scope. block is the +// lexical block enclosing the reference. If fn returns false the +// iteration is terminated and findLexicalRefs returns false. +func forEachLexicalRef(pkg Package, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool { + ok := true + var stack []ast.Node + + var visit func(n ast.Node) bool + visit = func(n ast.Node) bool { + if n == nil { + stack = stack[:len(stack)-1] // pop + return false + } + if !ok { + return false // bail out + } + + stack = append(stack, n) // push + switch n := n.(type) { + case *ast.Ident: + if pkg.GetTypesInfo().Uses[n] == obj { + block := enclosingBlock(pkg.GetTypesInfo(), stack) + if !fn(n, block) { + ok = false + } + } + return visit(nil) // pop stack + + case *ast.SelectorExpr: + // don't visit n.Sel + ast.Inspect(n.X, visit) + return visit(nil) // pop stack, don't descend + + case *ast.CompositeLit: + // Handle recursion ourselves for struct literals + // so we don't visit field identifiers. + tv, ok := pkg.GetTypesInfo().Types[n] + if !ok { + return visit(nil) // pop stack, don't descend + } + if _, ok := Deref(tv.Type).Underlying().(*types.Struct); ok { + if n.Type != nil { + ast.Inspect(n.Type, visit) + } + for _, elt := range n.Elts { + if kv, ok := elt.(*ast.KeyValueExpr); ok { + ast.Inspect(kv.Value, visit) + } else { + ast.Inspect(elt, visit) + } + } + return visit(nil) // pop stack, don't descend + } + } + return true + } + + for _, f := range pkg.GetSyntax() { + ast.Inspect(f, visit) + if len(stack) != 0 { + panic(stack) + } + if !ok { + break + } + } + return ok +} + +// enclosingBlock returns the innermost block enclosing the specified +// AST node, specified in the form of a path from the root of the file, +// [file...n]. +func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope { + for i := range stack { + n := stack[len(stack)-1-i] + // For some reason, go/types always associates a + // function's scope with its FuncType. + // TODO(adonovan): feature or a bug? + switch f := n.(type) { + case *ast.FuncDecl: + n = f.Type + case *ast.FuncLit: + n = f.Type + } + if b := info.Scopes[n]; b != nil { + return b + } + } + panic("no Scope for *ast.File") +} + +func (r *renamer) checkLabel(label *types.Label) { + // Check there are no identical labels in the function's label block. + // (Label blocks don't nest, so this is easy.) + if prev := label.Parent().Lookup(r.to); prev != nil { + r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name()) + r.errorf(prev.Pos(), "\twould conflict with this one") + } +} + +// checkStructField checks that the field renaming will not cause +// conflicts at its declaration, or ambiguity or changes to any selection. +func (r *renamer) checkStructField(from *types.Var) { + // Check that the struct declaration is free of field conflicts, + // and field/method conflicts. + + // go/types offers no easy way to get from a field (or interface + // method) to its declaring struct (or interface), so we must + // ascend the AST. + fromPkg, ok := r.packages[from.Pkg()] + if !ok { + return + } + pkg, _, path, _ := pathEnclosingInterval(fromPkg, from.Pos(), from.Pos()) + if pkg == nil || path == nil { + return + } + // path matches this pattern: + // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File] + + // Ascend to FieldList. + var i int + for { + if _, ok := path[i].(*ast.FieldList); ok { + break + } + i++ + } + i++ + tStruct := path[i].(*ast.StructType) + i++ + // Ascend past parens (unlikely). + for { + _, ok := path[i].(*ast.ParenExpr) + if !ok { + break + } + i++ + } + if spec, ok := path[i].(*ast.TypeSpec); ok { + // This struct is also a named type. + // We must check for direct (non-promoted) field/field + // and method/field conflicts. + named := pkg.GetTypesInfo().Defs[spec.Name].Type() + prev, indices, _ := types.LookupFieldOrMethod(named, true, pkg.GetTypes(), r.to) + if len(indices) == 1 { + r.errorf(from.Pos(), "renaming this field %q to %q", + from.Name(), r.to) + r.errorf(prev.Pos(), "\twould conflict with this %s", + objectKind(prev)) + return // skip checkSelections to avoid redundant errors + } + } else { + // This struct is not a named type. + // We need only check for direct (non-promoted) field/field conflicts. + T := pkg.GetTypesInfo().Types[tStruct].Type.Underlying().(*types.Struct) + for i := 0; i < T.NumFields(); i++ { + if prev := T.Field(i); prev.Name() == r.to { + r.errorf(from.Pos(), "renaming this field %q to %q", + from.Name(), r.to) + r.errorf(prev.Pos(), "\twould conflict with this field") + return // skip checkSelections to avoid redundant errors + } + } + } + + // Renaming an anonymous field requires renaming the type too. e.g. + // print(s.T) // if we rename T to U, + // type T int // this and + // var s struct {T} // this must change too. + if from.Anonymous() { + if named, ok := from.Type().(*types.Named); ok { + r.check(named.Obj()) + } else if named, ok := Deref(from.Type()).(*types.Named); ok { + r.check(named.Obj()) + } + } + + // Check integrity of existing (field and method) selections. + r.checkSelections(from) +} + +// checkSelection checks that all uses and selections that resolve to +// the specified object would continue to do so after the renaming. +func (r *renamer) checkSelections(from types.Object) { + for typ, pkg := range r.packages { + if id := someUse(pkg.GetTypesInfo(), from); id != nil { + if !r.checkExport(id, typ, from) { + return + } + } + + for syntax, sel := range pkg.GetTypesInfo().Selections { + // There may be extant selections of only the old + // name or only the new name, so we must check both. + // (If neither, the renaming is sound.) + // + // In both cases, we wish to compare the lengths + // of the implicit field path (Selection.Index) + // to see if the renaming would change it. + // + // If a selection that resolves to 'from', when renamed, + // would yield a path of the same or shorter length, + // this indicates ambiguity or a changed referent, + // analogous to same- or sub-block lexical conflict. + // + // If a selection using the name 'to' would + // yield a path of the same or shorter length, + // this indicates ambiguity or shadowing, + // analogous to same- or super-block lexical conflict. + + // TODO(adonovan): fix: derive from Types[syntax.X].Mode + // TODO(adonovan): test with pointer, value, addressable value. + isAddressable := true + + if sel.Obj() == from { + if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil { + // Renaming this existing selection of + // 'from' may block access to an existing + // type member named 'to'. + delta := len(indices) - len(sel.Index()) + if delta > 0 { + continue // no ambiguity + } + r.selectionConflict(from, delta, syntax, obj) + return + } + } else if sel.Obj().Name() == r.to { + if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from { + // Renaming 'from' may cause this existing + // selection of the name 'to' to change + // its meaning. + delta := len(indices) - len(sel.Index()) + if delta > 0 { + continue // no ambiguity + } + r.selectionConflict(from, -delta, syntax, sel.Obj()) + return + } + } + } + } +} + +func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) { + r.errorf(from.Pos(), "renaming this %s %q to %q", + objectKind(from), from.Name(), r.to) + + switch { + case delta < 0: + // analogous to sub-block conflict + r.errorf(syntax.Sel.Pos(), + "\twould change the referent of this selection") + r.errorf(obj.Pos(), "\tof this %s", objectKind(obj)) + case delta == 0: + // analogous to same-block conflict + r.errorf(syntax.Sel.Pos(), + "\twould make this reference ambiguous") + r.errorf(obj.Pos(), "\twith this %s", objectKind(obj)) + case delta > 0: + // analogous to super-block conflict + r.errorf(syntax.Sel.Pos(), + "\twould shadow this selection") + r.errorf(obj.Pos(), "\tof the %s declared here", + objectKind(obj)) + } +} + +// checkMethod performs safety checks for renaming a method. +// There are three hazards: +// - declaration conflicts +// - selection ambiguity/changes +// - entailed renamings of assignable concrete/interface types. +// +// We reject renamings initiated at concrete methods if it would +// change the assignability relation. For renamings of abstract +// methods, we rename all methods transitively coupled to it via +// assignability. +func (r *renamer) checkMethod(from *types.Func) { + // e.g. error.Error + if from.Pkg() == nil { + r.errorf(from.Pos(), "you cannot rename built-in method %s", from) + return + } + + // ASSIGNABILITY: We reject renamings of concrete methods that + // would break a 'satisfy' constraint; but renamings of abstract + // methods are allowed to proceed, and we rename affected + // concrete and abstract methods as necessary. It is the + // initial method that determines the policy. + + // Check for conflict at point of declaration. + // Check to ensure preservation of assignability requirements. + R := recv(from).Type() + if IsInterface(R) { + // Abstract method + + // declaration + prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to) + if prev != nil { + r.errorf(from.Pos(), "renaming this interface method %q to %q", + from.Name(), r.to) + r.errorf(prev.Pos(), "\twould conflict with this method") + return + } + + // Check all interfaces that embed this one for + // declaration conflicts too. + for _, pkg := range r.packages { + // Start with named interface types (better errors) + for _, obj := range pkg.GetTypesInfo().Defs { + if obj, ok := obj.(*types.TypeName); ok && IsInterface(obj.Type()) { + f, _, _ := types.LookupFieldOrMethod( + obj.Type(), false, from.Pkg(), from.Name()) + if f == nil { + continue + } + t, _, _ := types.LookupFieldOrMethod( + obj.Type(), false, from.Pkg(), r.to) + if t == nil { + continue + } + r.errorf(from.Pos(), "renaming this interface method %q to %q", + from.Name(), r.to) + r.errorf(t.Pos(), "\twould conflict with this method") + r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name()) + } + } + + // Now look at all literal interface types (includes named ones again). + for e, tv := range pkg.GetTypesInfo().Types { + if e, ok := e.(*ast.InterfaceType); ok { + _ = e + _ = tv.Type.(*types.Interface) + // TODO(adonovan): implement same check as above. + } + } + } + + // assignability + // + // Find the set of concrete or abstract methods directly + // coupled to abstract method 'from' by some + // satisfy.Constraint, and rename them too. + for key := range r.satisfy() { + // key = (lhs, rhs) where lhs is always an interface. + + lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) + if lsel == nil { + continue + } + rmethods := r.msets.MethodSet(key.RHS) + rsel := rmethods.Lookup(from.Pkg(), from.Name()) + if rsel == nil { + continue + } + + // If both sides have a method of this name, + // and one of them is m, the other must be coupled. + var coupled *types.Func + switch from { + case lsel.Obj(): + coupled = rsel.Obj().(*types.Func) + case rsel.Obj(): + coupled = lsel.Obj().(*types.Func) + default: + continue + } + + // We must treat concrete-to-interface + // constraints like an implicit selection C.f of + // each interface method I.f, and check that the + // renaming leaves the selection unchanged and + // unambiguous. + // + // Fun fact: the implicit selection of C.f + // type I interface{f()} + // type C struct{I} + // func (C) g() + // var _ I = C{} // here + // yields abstract method I.f. This can make error + // messages less than obvious. + // + if !IsInterface(key.RHS) { + // The logic below was derived from checkSelections. + + rtosel := rmethods.Lookup(from.Pkg(), r.to) + if rtosel != nil { + rto := rtosel.Obj().(*types.Func) + delta := len(rsel.Index()) - len(rtosel.Index()) + if delta < 0 { + continue // no ambiguity + } + + // TODO(adonovan): record the constraint's position. + keyPos := token.NoPos + + r.errorf(from.Pos(), "renaming this method %q to %q", + from.Name(), r.to) + if delta == 0 { + // analogous to same-block conflict + r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous", + r.to, key.RHS, key.LHS) + r.errorf(rto.Pos(), "\twith (%s).%s", + recv(rto).Type(), r.to) + } else { + // analogous to super-block conflict + r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s", + r.to, key.RHS, key.LHS) + r.errorf(coupled.Pos(), "\tfrom (%s).%s", + recv(coupled).Type(), r.to) + r.errorf(rto.Pos(), "\tto (%s).%s", + recv(rto).Type(), r.to) + } + return // one error is enough + } + } + + if !r.changeMethods { + // This should be unreachable. + r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from) + r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled) + r.errorf(from.Pos(), "\tPlease file a bug report") + return + } + + // Rename the coupled method to preserve assignability. + r.check(coupled) + } + } else { + // Concrete method + + // declaration + prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to) + if prev != nil && len(indices) == 1 { + r.errorf(from.Pos(), "renaming this method %q to %q", + from.Name(), r.to) + r.errorf(prev.Pos(), "\twould conflict with this %s", + objectKind(prev)) + return + } + + // assignability + // + // Find the set of abstract methods coupled to concrete + // method 'from' by some satisfy.Constraint, and rename + // them too. + // + // Coupling may be indirect, e.g. I.f <-> C.f via type D. + // + // type I interface {f()} + // type C int + // type (C) f() + // type D struct{C} + // var _ I = D{} + // + for key := range r.satisfy() { + // key = (lhs, rhs) where lhs is always an interface. + if IsInterface(key.RHS) { + continue + } + rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name()) + if rsel == nil || rsel.Obj() != from { + continue // rhs does not have the method + } + lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) + if lsel == nil { + continue + } + imeth := lsel.Obj().(*types.Func) + + // imeth is the abstract method (e.g. I.f) + // and key.RHS is the concrete coupling type (e.g. D). + if !r.changeMethods { + r.errorf(from.Pos(), "renaming this method %q to %q", + from.Name(), r.to) + var pos token.Pos + var iface string + + I := recv(imeth).Type() + if named, ok := I.(*types.Named); ok { + pos = named.Obj().Pos() + iface = "interface " + named.Obj().Name() + } else { + pos = from.Pos() + iface = I.String() + } + r.errorf(pos, "\twould make %s no longer assignable to %s", + key.RHS, iface) + r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)", + I, from.Name()) + return // one error is enough + } + + // Rename the coupled interface method to preserve assignability. + r.check(imeth) + } + } + + // Check integrity of existing (field and method) selections. + // We skip this if there were errors above, to avoid redundant errors. + r.checkSelections(from) +} + +func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { + // Reject cross-package references if r.to is unexported. + // (Such references may be qualified identifiers or field/method + // selections.) + if !ast.IsExported(r.to) && pkg != from.Pkg() { + r.errorf(from.Pos(), + "renaming %q to %q would make it unexported", + from.Name(), r.to) + r.errorf(id.Pos(), "\tbreaking references from packages such as %q", + pkg.Path()) + return false + } + return true +} + +// satisfy returns the set of interface satisfaction constraints. +func (r *renamer) satisfy() map[satisfy.Constraint]bool { + if r.satisfyConstraints == nil { + // Compute on demand: it's expensive. + var f satisfy.Finder + for _, pkg := range r.packages { + // From satisfy.Finder documentation: + // + // The package must be free of type errors, and + // info.{Defs,Uses,Selections,Types} must have been populated by the + // type-checker. + // + // Only proceed if all packages have no errors. + if pkg.HasListOrParseErrors() || pkg.HasTypeErrors() { + r.errorf(token.NoPos, // we don't have a position for this error. + "renaming %q to %q not possible because %q has errors", + r.from, r.to, pkg.PkgPath()) + return nil + } + f.Find(pkg.GetTypesInfo(), pkg.GetSyntax()) + } + r.satisfyConstraints = f.Result + } + return r.satisfyConstraints +} + +// -- helpers ---------------------------------------------------------- + +// recv returns the method's receiver. +func recv(meth *types.Func) *types.Var { + return meth.Type().(*types.Signature).Recv() +} + +// someUse returns an arbitrary use of obj within info. +func someUse(info *types.Info, obj types.Object) *ast.Ident { + for id, o := range info.Uses { + if o == obj { + return id + } + } + return nil +} + +// pathEnclosingInterval returns the Package, token.File, and ast.Node that +// contain source interval [start, end), and all the node's ancestors +// up to the AST root. It searches all ast.Files of all packages. +// exact is defined as for astutil.PathEnclosingInterval. +// +// The zero value is returned if not found. +func pathEnclosingInterval(pkg Package, start, end token.Pos) (resPkg Package, tokFile *token.File, path []ast.Node, exact bool) { + pkgs := []Package{pkg} + for _, f := range pkg.GetSyntax() { + for _, imp := range f.Imports { + if imp == nil { + continue + } + importPath := UnquoteImportPath(imp) + if importPath == "" { + continue + } + imported, err := pkg.ResolveImportPath(importPath) + if err != nil { + return nil, nil, nil, false + } + pkgs = append(pkgs, imported) + } + } + for _, p := range pkgs { + for _, f := range p.GetSyntax() { + if !f.Pos().IsValid() { + // This can happen if the parser saw + // too many errors and bailed out. + // (Use parser.AllErrors to prevent that.) + continue + } + tokFile := p.FileSet().File(f.Pos()) + if !tokenFileContainsPos(tokFile, start) { + continue + } + if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil { + return pkg, tokFile, path, exact + } + } + } + return nil, nil, nil, false +} + +// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos) +func tokenFileContainsPos(tf *token.File, pos token.Pos) bool { + p := int(pos) + base := tf.Base() + return base <= p && p <= base+tf.Size() +} + +func objectKind(obj types.Object) string { + if obj == nil { + return "nil object" + } + switch obj := obj.(type) { + case *types.PkgName: + return "imported package name" + case *types.TypeName: + return "type" + case *types.Var: + if obj.IsField() { + return "field" + } + case *types.Func: + if obj.Type().(*types.Signature).Recv() != nil { + return "method" + } + } + // label, func, var, const + return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types.")) +} + +// NB: for renamings, blank is not considered valid. +func isValidIdentifier(id string) bool { + if id == "" || id == "_" { + return false + } + for i, r := range id { + if !isLetter(r) && (i == 0 || !isDigit(r)) { + return false + } + } + return token.Lookup(id) == token.IDENT +} + +// isLocal reports whether obj is local to some function. +// Precondition: not a struct field or interface method. +func isLocal(obj types.Object) bool { + // [... 5=stmt 4=func 3=file 2=pkg 1=universe] + var depth int + for scope := obj.Parent(); scope != nil; scope = scope.Parent() { + depth++ + } + return depth >= 4 +} + +func isPackageLevel(obj types.Object) bool { + if obj == nil { + return false + } + return obj.Pkg().Scope().Lookup(obj.Name()) == obj +} + +// -- Plundered from go/scanner: --------------------------------------- + +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) +} + +func isDigit(ch rune) bool { + return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/signature_help.go b/gopls/golang_org_x_tools_gopls/lsp/source/signature_help.go new file mode 100644 index 0000000..8b166e6 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/signature_help.go @@ -0,0 +1,165 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "fmt" + "go/ast" + "go/token" + "go/types" + + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, position protocol.Position) (*protocol.SignatureInformation, int, error) { + ctx, done := event.Start(ctx, "source.SignatureHelp") + defer done() + + pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return nil, 0, fmt.Errorf("getting file for SignatureHelp: %w", err) + } + pos, err := pgf.Mapper.Pos(position) + if err != nil { + return nil, 0, err + } + // Find a call expression surrounding the query position. + var callExpr *ast.CallExpr + path, _ := astutil.PathEnclosingInterval(pgf.File, pos, pos) + if path == nil { + return nil, 0, fmt.Errorf("cannot find node enclosing position") + } +FindCall: + for _, node := range path { + switch node := node.(type) { + case *ast.CallExpr: + if pos >= node.Lparen && pos <= node.Rparen { + callExpr = node + break FindCall + } + case *ast.FuncLit, *ast.FuncType: + // The user is within an anonymous function, + // which may be the parameter to the *ast.CallExpr. + // Don't show signature help in this case. + return nil, 0, fmt.Errorf("no signature help within a function declaration") + case *ast.BasicLit: + if node.Kind == token.STRING { + return nil, 0, fmt.Errorf("no signature help within a string literal") + } + } + + } + if callExpr == nil || callExpr.Fun == nil { + return nil, 0, fmt.Errorf("cannot find an enclosing function") + } + + qf := Qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo()) + + // Get the object representing the function, if available. + // There is no object in certain cases such as calling a function returned by + // a function (e.g. "foo()()"). + var obj types.Object + switch t := callExpr.Fun.(type) { + case *ast.Ident: + obj = pkg.GetTypesInfo().ObjectOf(t) + case *ast.SelectorExpr: + obj = pkg.GetTypesInfo().ObjectOf(t.Sel) + } + + // Handle builtin functions separately. + if obj, ok := obj.(*types.Builtin); ok { + return builtinSignature(ctx, snapshot, callExpr, obj.Name(), pos) + } + + // Get the type information for the function being called. + sigType := pkg.GetTypesInfo().TypeOf(callExpr.Fun) + if sigType == nil { + return nil, 0, fmt.Errorf("cannot get type for Fun %[1]T (%[1]v)", callExpr.Fun) + } + + sig, _ := sigType.Underlying().(*types.Signature) + if sig == nil { + return nil, 0, fmt.Errorf("cannot find signature for Fun %[1]T (%[1]v)", callExpr.Fun) + } + + activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), pos) + + var ( + name string + comment *ast.CommentGroup + ) + if obj != nil { + declPkg, err := FindPackageFromPos(pkg, obj.Pos()) + if err != nil { + return nil, 0, err + } + node, _ := FindDeclAndField(declPkg.GetSyntax(), obj.Pos()) // may be nil + d, err := FindHoverContext(ctx, snapshot, pkg, obj, node, nil) + if err != nil { + return nil, 0, err + } + name = obj.Name() + comment = d.Comment + } else { + name = "func" + } + s := NewSignature(ctx, snapshot, pkg, sig, comment, qf) + paramInfo := make([]protocol.ParameterInformation, 0, len(s.params)) + for _, p := range s.params { + paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p}) + } + return &protocol.SignatureInformation{ + Label: name + s.Format(), + Documentation: s.doc, + Parameters: paramInfo, + }, activeParam, nil +} + +func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) { + sig, err := NewBuiltinSignature(ctx, snapshot, name) + if err != nil { + return nil, 0, err + } + paramInfo := make([]protocol.ParameterInformation, 0, len(sig.params)) + for _, p := range sig.params { + paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p}) + } + activeParam := activeParameter(callExpr, len(sig.params), sig.variadic, pos) + return &protocol.SignatureInformation{ + Label: sig.name + sig.Format(), + Documentation: sig.doc, + Parameters: paramInfo, + }, activeParam, nil + +} + +func activeParameter(callExpr *ast.CallExpr, numParams int, variadic bool, pos token.Pos) (activeParam int) { + if len(callExpr.Args) == 0 { + return 0 + } + // First, check if the position is even in the range of the arguments. + start, end := callExpr.Lparen, callExpr.Rparen + if !(start <= pos && pos <= end) { + return 0 + } + for _, expr := range callExpr.Args { + if start == token.NoPos { + start = expr.Pos() + } + end = expr.End() + if start <= pos && pos <= end { + break + } + // Don't advance the active parameter for the last parameter of a variadic function. + if !variadic || activeParam < numParams-1 { + activeParam++ + } + start = expr.Pos() + 1 // to account for commas + } + return activeParam +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/stub.go b/gopls/golang_org_x_tools_gopls/lsp/source/stub.go new file mode 100644 index 0000000..e4054d1 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/stub.go @@ -0,0 +1,332 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "bytes" + "context" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "go/types" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/astutil" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/analysis/stubmethods" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/safetoken" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +func stubSuggestedFixFunc(ctx context.Context, snapshot Snapshot, fh VersionedFileHandle, rng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) { + pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage) + if err != nil { + return nil, nil, fmt.Errorf("GetParsedFile: %w", err) + } + nodes, pos, err := getStubNodes(pgf, rng) + if err != nil { + return nil, nil, fmt.Errorf("getNodes: %w", err) + } + si := stubmethods.GetStubInfo(pkg.GetTypesInfo(), nodes, pos) + if si == nil { + return nil, nil, fmt.Errorf("nil interface request") + } + parsedConcreteFile, concreteFH, err := getStubFile(ctx, si.Concrete.Obj(), snapshot) + if err != nil { + return nil, nil, fmt.Errorf("getFile(concrete): %w", err) + } + var ( + methodsSrc []byte + stubImports []*stubImport // additional imports needed for method stubs + ) + if si.Interface.Pkg() == nil && si.Interface.Name() == "error" && si.Interface.Parent() == types.Universe { + methodsSrc = stubErr(ctx, parsedConcreteFile.File, si, snapshot) + } else { + methodsSrc, stubImports, err = stubMethods(ctx, parsedConcreteFile.File, si, snapshot) + } + if err != nil { + return nil, nil, fmt.Errorf("stubMethods: %w", err) + } + nodes, _ = astutil.PathEnclosingInterval(parsedConcreteFile.File, si.Concrete.Obj().Pos(), si.Concrete.Obj().Pos()) + concreteSrc, err := concreteFH.Read() + if err != nil { + return nil, nil, fmt.Errorf("error reading concrete file source: %w", err) + } + insertPos, err := safetoken.Offset(parsedConcreteFile.Tok, nodes[1].End()) + if err != nil || insertPos >= len(concreteSrc) { + return nil, nil, fmt.Errorf("insertion position is past the end of the file") + } + var buf bytes.Buffer + buf.Write(concreteSrc[:insertPos]) + buf.WriteByte('\n') + buf.Write(methodsSrc) + buf.Write(concreteSrc[insertPos:]) + fset := token.NewFileSet() + newF, err := parser.ParseFile(fset, parsedConcreteFile.File.Name.Name, buf.Bytes(), parser.ParseComments) + if err != nil { + return nil, nil, fmt.Errorf("could not reparse file: %w", err) + } + for _, imp := range stubImports { + astutil.AddNamedImport(fset, newF, imp.Name, imp.Path) + } + var source bytes.Buffer + err = format.Node(&source, fset, newF) + if err != nil { + return nil, nil, fmt.Errorf("format.Node: %w", err) + } + diffs := snapshot.View().Options().ComputeEdits(string(parsedConcreteFile.Src), source.String()) + tf := parsedConcreteFile.Mapper.TokFile + var edits []analysis.TextEdit + for _, edit := range diffs { + edits = append(edits, analysis.TextEdit{ + Pos: tf.Pos(edit.Start), + End: tf.Pos(edit.End), + NewText: []byte(edit.New), + }) + } + return snapshot.FileSet(), // from getStubFile + &analysis.SuggestedFix{TextEdits: edits}, + nil +} + +// stubMethods returns the Go code of all methods +// that implement the given interface +func stubMethods(ctx context.Context, concreteFile *ast.File, si *stubmethods.StubInfo, snapshot Snapshot) ([]byte, []*stubImport, error) { + ifacePkg, err := deducePkgFromTypes(ctx, snapshot, si.Interface) + if err != nil { + return nil, nil, err + } + si.Concrete.Obj().Type() + concMS := types.NewMethodSet(types.NewPointer(si.Concrete.Obj().Type())) + missing, err := missingMethods(ctx, snapshot, concMS, si.Concrete.Obj().Pkg(), si.Interface, ifacePkg, map[string]struct{}{}) + if err != nil { + return nil, nil, fmt.Errorf("missingMethods: %w", err) + } + if len(missing) == 0 { + return nil, nil, fmt.Errorf("no missing methods found") + } + var ( + stubImports []*stubImport + methodsBuffer bytes.Buffer + ) + for _, mi := range missing { + for _, m := range mi.missing { + // TODO(marwan-at-work): this should share the same logic with source.FormatVarType + // as it also accounts for type aliases. + sig := types.TypeString(m.Type(), stubmethods.RelativeToFiles(si.Concrete.Obj().Pkg(), concreteFile, mi.file, func(name, path string) { + for _, imp := range stubImports { + if imp.Name == name && imp.Path == path { + return + } + } + stubImports = append(stubImports, &stubImport{name, path}) + })) + _, err = methodsBuffer.Write(printStubMethod(methodData{ + Method: m.Name(), + Concrete: getStubReceiver(si), + Interface: deduceIfaceName(si.Concrete.Obj().Pkg(), si.Interface.Pkg(), si.Interface), + Signature: strings.TrimPrefix(sig, "func"), + })) + if err != nil { + return nil, nil, fmt.Errorf("error printing method: %w", err) + } + methodsBuffer.WriteRune('\n') + } + } + return methodsBuffer.Bytes(), stubImports, nil +} + +// stubErr reurns the Go code implementation +// of an error interface relevant to the +// concrete type +func stubErr(ctx context.Context, concreteFile *ast.File, si *stubmethods.StubInfo, snapshot Snapshot) []byte { + return printStubMethod(methodData{ + Method: "Error", + Interface: "error", + Concrete: getStubReceiver(si), + Signature: "() string", + }) +} + +// getStubReceiver returns the concrete type's name as a method receiver. +// It accounts for type parameters if they exist. +func getStubReceiver(si *stubmethods.StubInfo) string { + var concrete string + if si.Pointer { + concrete += "*" + } + concrete += si.Concrete.Obj().Name() + concrete += FormatTypeParams(typeparams.ForNamed(si.Concrete)) + return concrete +} + +type methodData struct { + Method string + Interface string + Concrete string + Signature string +} + +// printStubMethod takes methodData and returns Go code that represents the given method such as: +// +// // {{ .Method }} implements {{ .Interface }} +// func ({{ .Concrete }}) {{ .Method }}{{ .Signature }} { +// panic("unimplemented") +// } +func printStubMethod(md methodData) []byte { + var b bytes.Buffer + fmt.Fprintf(&b, "// %s implements %s\n", md.Method, md.Interface) + fmt.Fprintf(&b, "func (%s) %s%s {\n\t", md.Concrete, md.Method, md.Signature) + fmt.Fprintln(&b, `panic("unimplemented")`) + fmt.Fprintln(&b, "}") + return b.Bytes() +} + +func deducePkgFromTypes(ctx context.Context, snapshot Snapshot, ifaceObj types.Object) (Package, error) { + pkgs, err := snapshot.KnownPackages(ctx) + if err != nil { + return nil, err + } + for _, p := range pkgs { + if p.PkgPath() == PackagePath(ifaceObj.Pkg().Path()) { + return p, nil + } + } + return nil, fmt.Errorf("pkg %q not found", ifaceObj.Pkg().Path()) +} + +func deduceIfaceName(concretePkg, ifacePkg *types.Package, ifaceObj types.Object) string { + if concretePkg.Path() == ifacePkg.Path() { + return ifaceObj.Name() + } + return fmt.Sprintf("%s.%s", ifacePkg.Name(), ifaceObj.Name()) +} + +func getStubNodes(pgf *ParsedGoFile, pRng protocol.Range) ([]ast.Node, token.Pos, error) { + spn, err := pgf.Mapper.RangeSpan(pRng) + if err != nil { + return nil, 0, err + } + rng, err := spn.Range(pgf.Mapper.TokFile) + if err != nil { + return nil, 0, err + } + nodes, _ := astutil.PathEnclosingInterval(pgf.File, rng.Start, rng.End) + return nodes, rng.Start, nil +} + +/* +missingMethods takes a concrete type and returns any missing methods for the given interface as well as +any missing interface that might have been embedded to its parent. For example: + + type I interface { + io.Writer + Hello() + } + +returns + + []*missingInterface{ + { + iface: *types.Interface (io.Writer), + file: *ast.File: io.go, + missing []*types.Func{Write}, + }, + { + iface: *types.Interface (I), + file: *ast.File: myfile.go, + missing: []*types.Func{Hello} + }, + } +*/ +func missingMethods(ctx context.Context, snapshot Snapshot, concMS *types.MethodSet, concPkg *types.Package, ifaceObj types.Object, ifacePkg Package, visited map[string]struct{}) ([]*missingInterface, error) { + iface, ok := ifaceObj.Type().Underlying().(*types.Interface) + if !ok { + return nil, fmt.Errorf("expected %v to be an interface but got %T", iface, ifaceObj.Type().Underlying()) + } + missing := []*missingInterface{} + for i := 0; i < iface.NumEmbeddeds(); i++ { + eiface := iface.Embedded(i).Obj() + depPkg := ifacePkg + if path := PackagePath(eiface.Pkg().Path()); path != ifacePkg.PkgPath() { + // TODO(adonovan): I'm not sure what this is trying to do, but it + // looks wrong the in case of type aliases. + var err error + depPkg, err = ifacePkg.DirectDep(path) + if err != nil { + return nil, err + } + } + em, err := missingMethods(ctx, snapshot, concMS, concPkg, eiface, depPkg, visited) + if err != nil { + return nil, err + } + missing = append(missing, em...) + } + parsedFile, _, err := getStubFile(ctx, ifaceObj, snapshot) + if err != nil { + return nil, fmt.Errorf("error getting iface file: %w", err) + } + mi := &missingInterface{ + pkg: ifacePkg, + iface: iface, + file: parsedFile.File, + } + if mi.file == nil { + return nil, fmt.Errorf("could not find ast.File for %v", ifaceObj.Name()) + } + for i := 0; i < iface.NumExplicitMethods(); i++ { + method := iface.ExplicitMethod(i) + // if the concrete type does not have the interface method + if concMS.Lookup(concPkg, method.Name()) == nil { + if _, ok := visited[method.Name()]; !ok { + mi.missing = append(mi.missing, method) + visited[method.Name()] = struct{}{} + } + } + if sel := concMS.Lookup(concPkg, method.Name()); sel != nil { + implSig := sel.Type().(*types.Signature) + ifaceSig := method.Type().(*types.Signature) + if !types.Identical(ifaceSig, implSig) { + return nil, fmt.Errorf("mimsatched %q function signatures:\nhave: %s\nwant: %s", method.Name(), implSig, ifaceSig) + } + } + } + if len(mi.missing) > 0 { + missing = append(missing, mi) + } + return missing, nil +} + +// Token position information for obj.Pos and the ParsedGoFile result is in Snapshot.FileSet. +func getStubFile(ctx context.Context, obj types.Object, snapshot Snapshot) (*ParsedGoFile, VersionedFileHandle, error) { + objPos := snapshot.FileSet().Position(obj.Pos()) + objFile := span.URIFromPath(objPos.Filename) + objectFH := snapshot.FindFile(objFile) + _, goFile, err := GetParsedFile(ctx, snapshot, objectFH, WidestPackage) + if err != nil { + return nil, nil, fmt.Errorf("GetParsedFile: %w", err) + } + return goFile, objectFH, nil +} + +// missingInterface represents an interface +// that has all or some of its methods missing +// from the destination concrete type +type missingInterface struct { + iface *types.Interface + file *ast.File + pkg Package + missing []*types.Func +} + +// stubImport represents a newly added import +// statement to the concrete type. If name is not +// empty, then that import is required to have that name. +type stubImport struct{ Name, Path string } diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/symbols.go b/gopls/golang_org_x_tools_gopls/lsp/source/symbols.go new file mode 100644 index 0000000..2a2ef57 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/symbols.go @@ -0,0 +1,235 @@ +// 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 source + +import ( + "context" + "fmt" + "go/ast" + "go/token" + "go/types" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/lsppos" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" +) + +func DocumentSymbols(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.DocumentSymbol, error) { + ctx, done := event.Start(ctx, "source.DocumentSymbols") + defer done() + + content, err := fh.Read() + if err != nil { + return nil, err + } + + pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) + if err != nil { + return nil, fmt.Errorf("getting file for DocumentSymbols: %w", err) + } + + m := lsppos.NewTokenMapper(content, pgf.Tok) + + // Build symbols for file declarations. When encountering a declaration with + // errors (typically because positions are invalid), we skip the declaration + // entirely. VS Code fails to show any symbols if one of the top-level + // symbols is missing position information. + var symbols []protocol.DocumentSymbol + for _, decl := range pgf.File.Decls { + switch decl := decl.(type) { + case *ast.FuncDecl: + if decl.Name.Name == "_" { + continue + } + fs, err := funcSymbol(m, decl) + if err == nil { + // If function is a method, prepend the type of the method. + if decl.Recv != nil && len(decl.Recv.List) > 0 { + fs.Name = fmt.Sprintf("(%s).%s", types.ExprString(decl.Recv.List[0].Type), fs.Name) + } + symbols = append(symbols, fs) + } + case *ast.GenDecl: + for _, spec := range decl.Specs { + switch spec := spec.(type) { + case *ast.TypeSpec: + if spec.Name.Name == "_" { + continue + } + ts, err := typeSymbol(m, spec) + if err == nil { + symbols = append(symbols, ts) + } + case *ast.ValueSpec: + for _, name := range spec.Names { + if name.Name == "_" { + continue + } + vs, err := varSymbol(m, spec, name, decl.Tok == token.CONST) + if err == nil { + symbols = append(symbols, vs) + } + } + } + } + } + } + return symbols, nil +} + +func funcSymbol(m *lsppos.TokenMapper, decl *ast.FuncDecl) (protocol.DocumentSymbol, error) { + s := protocol.DocumentSymbol{ + Name: decl.Name.Name, + Kind: protocol.Function, + } + if decl.Recv != nil { + s.Kind = protocol.Method + } + var err error + s.Range, err = m.Range(decl.Pos(), decl.End()) + if err != nil { + return protocol.DocumentSymbol{}, err + } + s.SelectionRange, err = m.Range(decl.Name.Pos(), decl.Name.End()) + if err != nil { + return protocol.DocumentSymbol{}, err + } + s.Detail = types.ExprString(decl.Type) + return s, nil +} + +func typeSymbol(m *lsppos.TokenMapper, spec *ast.TypeSpec) (protocol.DocumentSymbol, error) { + s := protocol.DocumentSymbol{ + Name: spec.Name.Name, + } + var err error + s.Range, err = m.NodeRange(spec) + if err != nil { + return protocol.DocumentSymbol{}, err + } + s.SelectionRange, err = m.NodeRange(spec.Name) + if err != nil { + return protocol.DocumentSymbol{}, err + } + s.Kind, s.Detail, s.Children = typeDetails(m, spec.Type) + return s, nil +} + +func typeDetails(m *lsppos.TokenMapper, typExpr ast.Expr) (kind protocol.SymbolKind, detail string, children []protocol.DocumentSymbol) { + switch typExpr := typExpr.(type) { + case *ast.StructType: + kind = protocol.Struct + children = fieldListSymbols(m, typExpr.Fields, protocol.Field) + if len(children) > 0 { + detail = "struct{...}" + } else { + detail = "struct{}" + } + + // Find interface methods and embedded types. + case *ast.InterfaceType: + kind = protocol.Interface + children = fieldListSymbols(m, typExpr.Methods, protocol.Method) + if len(children) > 0 { + detail = "interface{...}" + } else { + detail = "interface{}" + } + + case *ast.FuncType: + kind = protocol.Function + detail = types.ExprString(typExpr) + + default: + kind = protocol.Class // catch-all, for cases where we don't know the kind syntactically + detail = types.ExprString(typExpr) + } + return +} + +func fieldListSymbols(m *lsppos.TokenMapper, fields *ast.FieldList, fieldKind protocol.SymbolKind) []protocol.DocumentSymbol { + if fields == nil { + return nil + } + + var symbols []protocol.DocumentSymbol + for _, field := range fields.List { + detail, children := "", []protocol.DocumentSymbol(nil) + if field.Type != nil { + _, detail, children = typeDetails(m, field.Type) + } + if len(field.Names) == 0 { // embedded interface or struct field + // By default, use the formatted type details as the name of this field. + // This handles potentially invalid syntax, as well as type embeddings in + // interfaces. + child := protocol.DocumentSymbol{ + Name: detail, + Kind: protocol.Field, // consider all embeddings to be fields + Children: children, + } + + // If the field is a valid embedding, promote the type name to field + // name. + selection := field.Type + if id := embeddedIdent(field.Type); id != nil { + child.Name = id.Name + child.Detail = detail + selection = id + } + + if rng, err := m.NodeRange(field.Type); err == nil { + child.Range = rng + } + if rng, err := m.NodeRange(selection); err == nil { + child.SelectionRange = rng + } + + symbols = append(symbols, child) + } else { + for _, name := range field.Names { + child := protocol.DocumentSymbol{ + Name: name.Name, + Kind: fieldKind, + Detail: detail, + Children: children, + } + + if rng, err := m.NodeRange(field); err == nil { + child.Range = rng + } + if rng, err := m.NodeRange(name); err == nil { + child.SelectionRange = rng + } + + symbols = append(symbols, child) + } + } + + } + return symbols +} + +func varSymbol(m *lsppos.TokenMapper, spec *ast.ValueSpec, name *ast.Ident, isConst bool) (protocol.DocumentSymbol, error) { + s := protocol.DocumentSymbol{ + Name: name.Name, + Kind: protocol.Variable, + } + if isConst { + s.Kind = protocol.Constant + } + var err error + s.Range, err = m.NodeRange(spec) + if err != nil { + return protocol.DocumentSymbol{}, err + } + s.SelectionRange, err = m.NodeRange(name) + if err != nil { + return protocol.DocumentSymbol{}, err + } + if spec.Type != nil { // type may be missing from the syntax + _, s.Detail, s.Children = typeDetails(m, spec.Type) + } + return s, nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/types_format.go b/gopls/golang_org_x_tools_gopls/lsp/source/types_format.go new file mode 100644 index 0000000..2a90427 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/types_format.go @@ -0,0 +1,451 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "bytes" + "context" + "fmt" + "go/ast" + "go/doc" + "go/printer" + "go/token" + "go/types" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event/tag" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +// FormatType returns the detail and kind for a types.Type. +func FormatType(typ types.Type, qf types.Qualifier) (detail string, kind protocol.CompletionItemKind) { + if types.IsInterface(typ) { + detail = "interface{...}" + kind = protocol.InterfaceCompletion + } else if _, ok := typ.(*types.Struct); ok { + detail = "struct{...}" + kind = protocol.StructCompletion + } else if typ != typ.Underlying() { + detail, kind = FormatType(typ.Underlying(), qf) + } else { + detail = types.TypeString(typ, qf) + kind = protocol.ClassCompletion + } + return detail, kind +} + +type signature struct { + name, doc string + typeParams, params, results []string + variadic bool + needResultParens bool +} + +func (s *signature) Format() string { + var b strings.Builder + b.WriteByte('(') + for i, p := range s.params { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(p) + } + b.WriteByte(')') + + // Add space between parameters and results. + if len(s.results) > 0 { + b.WriteByte(' ') + } + if s.needResultParens { + b.WriteByte('(') + } + for i, r := range s.results { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(r) + } + if s.needResultParens { + b.WriteByte(')') + } + return b.String() +} + +func (s *signature) TypeParams() []string { + return s.typeParams +} + +func (s *signature) Params() []string { + return s.params +} + +// NewBuiltinSignature returns signature for the builtin object with a given +// name, if a builtin object with the name exists. +func NewBuiltinSignature(ctx context.Context, s Snapshot, name string) (*signature, error) { + fset := s.FileSet() + builtin, err := s.BuiltinFile(ctx) + if err != nil { + return nil, err + } + obj := builtin.File.Scope.Lookup(name) + if obj == nil { + return nil, fmt.Errorf("no builtin object for %s", name) + } + decl, ok := obj.Decl.(*ast.FuncDecl) + if !ok { + return nil, fmt.Errorf("no function declaration for builtin: %s", name) + } + if decl.Type == nil { + return nil, fmt.Errorf("no type for builtin decl %s", decl.Name) + } + var variadic bool + if decl.Type.Params.List != nil { + numParams := len(decl.Type.Params.List) + lastParam := decl.Type.Params.List[numParams-1] + if _, ok := lastParam.Type.(*ast.Ellipsis); ok { + variadic = true + } + } + params, _ := formatFieldList(ctx, fset, decl.Type.Params, variadic) + results, needResultParens := formatFieldList(ctx, fset, decl.Type.Results, false) + d := decl.Doc.Text() + switch s.View().Options().HoverKind { + case SynopsisDocumentation: + d = doc.Synopsis(d) + case NoDocumentation: + d = "" + } + return &signature{ + doc: d, + name: name, + needResultParens: needResultParens, + params: params, + results: results, + variadic: variadic, + }, nil +} + +var replacer = strings.NewReplacer( + `ComplexType`, `complex128`, + `FloatType`, `float64`, + `IntegerType`, `int`, +) + +func formatFieldList(ctx context.Context, fset *token.FileSet, list *ast.FieldList, variadic bool) ([]string, bool) { + if list == nil { + return nil, false + } + var writeResultParens bool + var result []string + for i := 0; i < len(list.List); i++ { + if i >= 1 { + writeResultParens = true + } + p := list.List[i] + cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 4} + b := &bytes.Buffer{} + if err := cfg.Fprint(b, fset, p.Type); err != nil { + event.Error(ctx, "unable to print type", nil, tag.Type.Of(p.Type)) + continue + } + typ := replacer.Replace(b.String()) + if len(p.Names) == 0 { + result = append(result, typ) + } + for _, name := range p.Names { + if name.Name != "" { + if i == 0 { + writeResultParens = true + } + result = append(result, fmt.Sprintf("%s %s", name.Name, typ)) + } else { + result = append(result, typ) + } + } + } + if variadic { + result[len(result)-1] = strings.Replace(result[len(result)-1], "[]", "...", 1) + } + return result, writeResultParens +} + +// FormatTypeParams turns TypeParamList into its Go representation, such as: +// [T, Y]. Note that it does not print constraints as this is mainly used for +// formatting type params in method receivers. +func FormatTypeParams(tparams *typeparams.TypeParamList) string { + if tparams == nil || tparams.Len() == 0 { + return "" + } + var buf bytes.Buffer + buf.WriteByte('[') + for i := 0; i < tparams.Len(); i++ { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(tparams.At(i).Obj().Name()) + } + buf.WriteByte(']') + return buf.String() +} + +// NewSignature returns formatted signature for a types.Signature struct. +func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier) *signature { + var tparams []string + tpList := typeparams.ForSignature(sig) + for i := 0; i < tpList.Len(); i++ { + tparam := tpList.At(i) + // TODO: is it possible to reuse the logic from FormatVarType here? + s := tparam.Obj().Name() + " " + tparam.Constraint().String() + tparams = append(tparams, s) + } + + params := make([]string, 0, sig.Params().Len()) + for i := 0; i < sig.Params().Len(); i++ { + el := sig.Params().At(i) + typ := FormatVarType(pkg, el, qf) + p := typ + if el.Name() != "" { + p = el.Name() + " " + typ + } + params = append(params, p) + } + + var needResultParens bool + results := make([]string, 0, sig.Results().Len()) + for i := 0; i < sig.Results().Len(); i++ { + if i >= 1 { + needResultParens = true + } + el := sig.Results().At(i) + typ := FormatVarType(pkg, el, qf) + if el.Name() == "" { + results = append(results, typ) + } else { + if i == 0 { + needResultParens = true + } + results = append(results, el.Name()+" "+typ) + } + } + var d string + if comment != nil { + d = comment.Text() + } + switch s.View().Options().HoverKind { + case SynopsisDocumentation: + d = doc.Synopsis(d) + case NoDocumentation: + d = "" + } + return &signature{ + doc: d, + typeParams: tparams, + params: params, + results: results, + variadic: sig.Variadic(), + needResultParens: needResultParens, + } +} + +// FormatVarType formats a *types.Var, accounting for type aliases. +// To do this, it looks in the AST of the file in which the object is declared. +// On any errors, it always falls back to types.TypeString. +func FormatVarType(srcpkg Package, obj *types.Var, qf types.Qualifier) string { + pkg, err := FindPackageFromPos(srcpkg, obj.Pos()) + if err != nil { + return types.TypeString(obj.Type(), qf) + } + + _, field := FindDeclAndField(pkg.GetSyntax(), obj.Pos()) + if field == nil { + return types.TypeString(obj.Type(), qf) + } + expr := field.Type + + // If the given expr refers to a type parameter, then use the + // object's Type instead of the type parameter declaration. This helps + // format the instantiated type as opposed to the original undeclared + // generic type. + if typeparams.IsTypeParam(pkg.GetTypesInfo().Types[expr].Type) { + return types.TypeString(obj.Type(), qf) + } + + // The type names in the AST may not be correctly qualified. + // Determine the package name to use based on the package that originated + // the query and the package in which the type is declared. + // We then qualify the value by cloning the AST node and editing it. + clonedInfo := make(map[token.Pos]*types.PkgName) + qualified := cloneExpr(expr, pkg.GetTypesInfo(), clonedInfo) + + // If the request came from a different package than the one in which the + // types are defined, we may need to modify the qualifiers. + qualified = qualifyExpr(qualified, srcpkg, pkg, clonedInfo, qf) + fmted := FormatNode(srcpkg.FileSet(), qualified) + return fmted +} + +// qualifyExpr applies the "pkgName." prefix to any *ast.Ident in the expr. +func qualifyExpr(expr ast.Expr, srcpkg, pkg Package, clonedInfo map[token.Pos]*types.PkgName, qf types.Qualifier) ast.Expr { + ast.Inspect(expr, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.ArrayType, *ast.ChanType, *ast.Ellipsis, + *ast.FuncType, *ast.MapType, *ast.ParenExpr, + *ast.StarExpr, *ast.StructType, *ast.FieldList, *ast.Field: + // These are the only types that are cloned by cloneExpr below, + // so these are the only types that we can traverse and potentially + // modify. This is not an ideal approach, but it works for now. + + // TODO(rFindley): can we eliminate this filtering entirely? This caused + // bugs in the past (golang/go#50539) + return true + case *ast.SelectorExpr: + // We may need to change any selectors in which the X is a package + // name and the Sel is exported. + x, ok := n.X.(*ast.Ident) + if !ok { + return false + } + obj, ok := clonedInfo[x.Pos()] + if !ok { + return false + } + x.Name = qf(obj.Imported()) + return false + case *ast.Ident: + if srcpkg == pkg { + return false + } + // Only add the qualifier if the identifier is exported. + if ast.IsExported(n.Name) { + pkgName := qf(pkg.GetTypes()) + n.Name = pkgName + "." + n.Name + } + } + return false + }) + return expr +} + +// cloneExpr only clones expressions that appear in the parameters or return +// values of a function declaration. The original expression may be returned +// to the caller in 2 cases: +// +// 1. The expression has no pointer fields. +// 2. The expression cannot appear in an *ast.FuncType, making it +// unnecessary to clone. +// +// This function also keeps track of selector expressions in which the X is a +// package name and marks them in a map along with their type information, so +// that this information can be used when rewriting the expression. +// +// NOTE: This function is tailored to the use case of qualifyExpr, and should +// be used with caution. +func cloneExpr(expr ast.Expr, info *types.Info, clonedInfo map[token.Pos]*types.PkgName) ast.Expr { + switch expr := expr.(type) { + case *ast.ArrayType: + return &ast.ArrayType{ + Lbrack: expr.Lbrack, + Elt: cloneExpr(expr.Elt, info, clonedInfo), + Len: expr.Len, + } + case *ast.ChanType: + return &ast.ChanType{ + Arrow: expr.Arrow, + Begin: expr.Begin, + Dir: expr.Dir, + Value: cloneExpr(expr.Value, info, clonedInfo), + } + case *ast.Ellipsis: + return &ast.Ellipsis{ + Ellipsis: expr.Ellipsis, + Elt: cloneExpr(expr.Elt, info, clonedInfo), + } + case *ast.FuncType: + return &ast.FuncType{ + Func: expr.Func, + Params: cloneFieldList(expr.Params, info, clonedInfo), + Results: cloneFieldList(expr.Results, info, clonedInfo), + } + case *ast.Ident: + return cloneIdent(expr) + case *ast.MapType: + return &ast.MapType{ + Map: expr.Map, + Key: cloneExpr(expr.Key, info, clonedInfo), + Value: cloneExpr(expr.Value, info, clonedInfo), + } + case *ast.ParenExpr: + return &ast.ParenExpr{ + Lparen: expr.Lparen, + Rparen: expr.Rparen, + X: cloneExpr(expr.X, info, clonedInfo), + } + case *ast.SelectorExpr: + s := &ast.SelectorExpr{ + Sel: cloneIdent(expr.Sel), + X: cloneExpr(expr.X, info, clonedInfo), + } + if x, ok := expr.X.(*ast.Ident); ok && ast.IsExported(expr.Sel.Name) { + if obj, ok := info.ObjectOf(x).(*types.PkgName); ok { + clonedInfo[s.X.Pos()] = obj + } + } + return s + case *ast.StarExpr: + return &ast.StarExpr{ + Star: expr.Star, + X: cloneExpr(expr.X, info, clonedInfo), + } + case *ast.StructType: + return &ast.StructType{ + Struct: expr.Struct, + Fields: cloneFieldList(expr.Fields, info, clonedInfo), + Incomplete: expr.Incomplete, + } + default: + return expr + } +} + +func cloneFieldList(fl *ast.FieldList, info *types.Info, clonedInfo map[token.Pos]*types.PkgName) *ast.FieldList { + if fl == nil { + return nil + } + if fl.List == nil { + return &ast.FieldList{ + Closing: fl.Closing, + Opening: fl.Opening, + } + } + list := make([]*ast.Field, 0, len(fl.List)) + for _, f := range fl.List { + var names []*ast.Ident + for _, n := range f.Names { + names = append(names, cloneIdent(n)) + } + list = append(list, &ast.Field{ + Comment: f.Comment, + Doc: f.Doc, + Names: names, + Tag: f.Tag, + Type: cloneExpr(f.Type, info, clonedInfo), + }) + } + return &ast.FieldList{ + Closing: fl.Closing, + Opening: fl.Opening, + List: list, + } +} + +func cloneIdent(ident *ast.Ident) *ast.Ident { + return &ast.Ident{ + NamePos: ident.NamePos, + Name: ident.Name, + Obj: ident.Obj, + } +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/util.go b/gopls/golang_org_x_tools_gopls/lsp/source/util.go new file mode 100644 index 0000000..64fc6ed --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/util.go @@ -0,0 +1,605 @@ +// 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 source + +import ( + "context" + "fmt" + "go/ast" + "go/printer" + "go/token" + "go/types" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/bug" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/typeparams" +) + +// MappedRange provides mapped protocol.Range for a span.Range, accounting for +// UTF-16 code points. +// +// TOOD(adonovan): stop treating //line directives specially, and +// eliminate this type. All callers need either m, or a protocol.Range. +type MappedRange struct { + spanRange span.Range // the range in the compiled source (package.CompiledGoFiles) + m *protocol.ColumnMapper // a mapper of the edited source (package.GoFiles) +} + +// NewMappedRange returns a MappedRange for the given file and valid start/end token.Pos. +// +// By convention, start and end are assumed to be positions in the compiled (== +// type checked) source, whereas the column mapper m maps positions in the +// user-edited source. Note that these may not be the same, as when using goyacc or CGo: +// CompiledGoFiles contains generated files, whose positions (via +// token.File.Position) point to locations in the edited file -- the file +// containing `import "C"`. +func NewMappedRange(file *token.File, m *protocol.ColumnMapper, start, end token.Pos) MappedRange { + mapped := m.TokFile.Name() + adjusted := file.PositionFor(start, true) // adjusted position + if adjusted.Filename != mapped { + bug.Reportf("mapped file %q does not match start position file %q", mapped, adjusted.Filename) + } + return MappedRange{ + spanRange: span.NewRange(file, start, end), + m: m, + } +} + +// Range returns the LSP range in the edited source. +// +// See the documentation of NewMappedRange for information on edited vs +// compiled source. +func (s MappedRange) Range() (protocol.Range, error) { + if s.m == nil { + return protocol.Range{}, bug.Errorf("invalid range") + } + spn, err := s.Span() + if err != nil { + return protocol.Range{}, err + } + return s.m.Range(spn) +} + +// Span returns the span corresponding to the mapped range in the edited +// source. +// +// See the documentation of NewMappedRange for information on edited vs +// compiled source. +func (s MappedRange) Span() (span.Span, error) { + // In the past, some code-paths have relied on Span returning an error if s + // is the zero value (i.e. s.m is nil). But this should be treated as a bug: + // observe that s.URI() would panic in this case. + if s.m == nil { + return span.Span{}, bug.Errorf("invalid range") + } + return span.FileSpan(s.spanRange.TokFile, s.m.TokFile, s.spanRange.Start, s.spanRange.End) +} + +// URI returns the URI of the edited file. +// +// See the documentation of NewMappedRange for information on edited vs +// compiled source. +func (s MappedRange) URI() span.URI { + return s.m.URI +} + +// GetParsedFile is a convenience function that extracts the Package and +// ParsedGoFile for a file in a Snapshot. pkgPolicy is one of NarrowestPackage/ +// WidestPackage. +func GetParsedFile(ctx context.Context, snapshot Snapshot, fh FileHandle, pkgPolicy PackageFilter) (Package, *ParsedGoFile, error) { + pkg, err := snapshot.PackageForFile(ctx, fh.URI(), TypecheckWorkspace, pkgPolicy) + if err != nil { + return nil, nil, err + } + pgh, err := pkg.File(fh.URI()) + return pkg, pgh, err +} + +func IsGenerated(ctx context.Context, snapshot Snapshot, uri span.URI) bool { + fh, err := snapshot.GetFile(ctx, uri) + if err != nil { + return false + } + pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader) + if err != nil { + return false + } + for _, commentGroup := range pgf.File.Comments { + for _, comment := range commentGroup.List { + if matched := generatedRx.MatchString(comment.Text); matched { + // Check if comment is at the beginning of the line in source. + if pgf.Tok.Position(comment.Slash).Column == 1 { + return true + } + } + } + } + return false +} + +func objToMappedRange(pkg Package, obj types.Object) (MappedRange, error) { + nameLen := len(obj.Name()) + if pkgName, ok := obj.(*types.PkgName); ok { + // An imported Go package has a package-local, unqualified name. + // When the name matches the imported package name, there is no + // identifier in the import spec with the local package name. + // + // For example: + // import "go/ast" // name "ast" matches package name + // import a "go/ast" // name "a" does not match package name + // + // When the identifier does not appear in the source, have the range + // of the object be the import path, including quotes. + if pkgName.Imported().Name() == pkgName.Name() { + nameLen = len(pkgName.Imported().Path()) + len(`""`) + } + } + return posToMappedRange(pkg, obj.Pos(), obj.Pos()+token.Pos(nameLen)) +} + +// posToMappedRange returns the MappedRange for the given [start, end) span, +// which must be among the transitive dependencies of pkg. +func posToMappedRange(pkg Package, pos, end token.Pos) (MappedRange, error) { + if !pos.IsValid() { + return MappedRange{}, fmt.Errorf("invalid start position") + } + if !end.IsValid() { + return MappedRange{}, fmt.Errorf("invalid end position") + } + + fset := pkg.FileSet() + tokFile := fset.File(pos) + // Subtle: it is not safe to simplify this to tokFile.Name + // because, due to //line directives, a Position within a + // token.File may have a different filename than the File itself. + logicalFilename := tokFile.Position(pos).Filename + pgf, _, err := findFileInDeps(pkg, span.URIFromPath(logicalFilename)) + if err != nil { + return MappedRange{}, err + } + // It is problematic that pgf.Mapper (from the parsed Go file) is + // accompanied here not by pgf.Tok but by tokFile from the global + // FileSet, which is a distinct token.File that doesn't + // contain [pos,end). + // + // This is done because tokFile is the *token.File for the compiled go file + // containing pos, whereas Mapper is the UTF16 mapper for the go file pointed + // to by line directives. + // + // TODO(golang/go#55043): clean this up. + return NewMappedRange(tokFile, pgf.Mapper, pos, end), nil +} + +// FindPackageFromPos returns the Package for the given position, which must be +// among the transitive dependencies of pkg. +// +// TODO(rfindley): is this the best factoring of this API? This function is +// really a trivial wrapper around findFileInDeps, which may be a more useful +// function to expose. +func FindPackageFromPos(pkg Package, pos token.Pos) (Package, error) { + if !pos.IsValid() { + return nil, fmt.Errorf("invalid position") + } + fileName := pkg.FileSet().File(pos).Name() + uri := span.URIFromPath(fileName) + _, pkg, err := findFileInDeps(pkg, uri) + return pkg, err +} + +// Matches cgo generated comment as well as the proposed standard: +// +// https://golang.org/s/generatedcode +var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`) + +// FileKindForLang returns the file kind associated with the given language ID, +// or UnknownKind if the language ID is not recognized. +func FileKindForLang(langID string) FileKind { + switch langID { + case "go": + return Go + case "go.mod": + return Mod + case "go.sum": + return Sum + case "tmpl", "gotmpl": + return Tmpl + case "go.work": + return Work + default: + return UnknownKind + } +} + +func (k FileKind) String() string { + switch k { + case Go: + return "go" + case Mod: + return "go.mod" + case Sum: + return "go.sum" + case Tmpl: + return "tmpl" + case Work: + return "go.work" + default: + return fmt.Sprintf("unk%d", k) + } +} + +// nodeAtPos returns the index and the node whose position is contained inside +// the node list. +func nodeAtPos(nodes []ast.Node, pos token.Pos) (ast.Node, int) { + if nodes == nil { + return nil, -1 + } + for i, node := range nodes { + if node.Pos() <= pos && pos <= node.End() { + return node, i + } + } + return nil, -1 +} + +// IsInterface returns if a types.Type is an interface +func IsInterface(T types.Type) bool { + return T != nil && types.IsInterface(T) +} + +// FormatNode returns the "pretty-print" output for an ast node. +func FormatNode(fset *token.FileSet, n ast.Node) string { + var buf strings.Builder + if err := printer.Fprint(&buf, fset, n); err != nil { + return "" + } + return buf.String() +} + +// Deref returns a pointer's element type, traversing as many levels as needed. +// Otherwise it returns typ. +// +// It can return a pointer type for cyclic types (see golang/go#45510). +func Deref(typ types.Type) types.Type { + var seen map[types.Type]struct{} + for { + p, ok := typ.Underlying().(*types.Pointer) + if !ok { + return typ + } + if _, ok := seen[p.Elem()]; ok { + return typ + } + + typ = p.Elem() + + if seen == nil { + seen = make(map[types.Type]struct{}) + } + seen[typ] = struct{}{} + } +} + +func SortDiagnostics(d []*Diagnostic) { + sort.Slice(d, func(i int, j int) bool { + return CompareDiagnostic(d[i], d[j]) < 0 + }) +} + +func CompareDiagnostic(a, b *Diagnostic) int { + if r := protocol.CompareRange(a.Range, b.Range); r != 0 { + return r + } + if a.Source < b.Source { + return -1 + } + if a.Source > b.Source { + return +1 + } + if a.Message < b.Message { + return -1 + } + if a.Message > b.Message { + return +1 + } + return 0 +} + +// findFileInDeps finds uri in pkg or its dependencies. +func findFileInDeps(pkg Package, uri span.URI) (*ParsedGoFile, Package, error) { + queue := []Package{pkg} + seen := make(map[PackageID]bool) + + for len(queue) > 0 { + pkg := queue[0] + queue = queue[1:] + seen[pkg.ID()] = true + + if pgf, err := pkg.File(uri); err == nil { + return pgf, pkg, nil + } + for _, dep := range pkg.Imports() { + if !seen[dep.ID()] { + queue = append(queue, dep) + } + } + } + return nil, nil, fmt.Errorf("no file for %s in package %s", uri, pkg.ID()) +} + +// UnquoteImportPath returns the unquoted import path of s, +// or "" if the path is not properly quoted. +func UnquoteImportPath(s *ast.ImportSpec) ImportPath { + path, err := strconv.Unquote(s.Path.Value) + if err != nil { + return "" + } + return ImportPath(path) +} + +// NodeContains returns true if a node encloses a given position pos. +func NodeContains(n ast.Node, pos token.Pos) bool { + return n != nil && n.Pos() <= pos && pos <= n.End() +} + +// CollectScopes returns all scopes in an ast path, ordered as innermost scope +// first. +func CollectScopes(info *types.Info, path []ast.Node, pos token.Pos) []*types.Scope { + // scopes[i], where i= len(c) { + return false + } + for i := 0; i <= colon+1; i++ { + if i == colon { + continue + } + b := c[i] + if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') { + return false + } + } + return true +} + +// honorSymlinks toggles whether or not we consider symlinks when comparing +// file or directory URIs. +const honorSymlinks = false + +func CompareURI(left, right span.URI) int { + if honorSymlinks { + return span.CompareURI(left, right) + } + if left == right { + return 0 + } + if left < right { + return -1 + } + return 1 +} + +// InDir checks whether path is in the file tree rooted at dir. +// InDir makes some effort to succeed even in the presence of symbolic links. +// +// Copied and slightly adjusted from go/src/cmd/go/internal/search/search.go. +func InDir(dir, path string) bool { + if InDirLex(dir, path) { + return true + } + if !honorSymlinks { + return false + } + xpath, err := filepath.EvalSymlinks(path) + if err != nil || xpath == path { + xpath = "" + } else { + if InDirLex(dir, xpath) { + return true + } + } + + xdir, err := filepath.EvalSymlinks(dir) + if err == nil && xdir != dir { + if InDirLex(xdir, path) { + return true + } + if xpath != "" { + if InDirLex(xdir, xpath) { + return true + } + } + } + return false +} + +// InDirLex is like inDir but only checks the lexical form of the file names. +// It does not consider symbolic links. +// +// Copied from go/src/cmd/go/internal/search/search.go. +func InDirLex(dir, path string) bool { + pv := strings.ToUpper(filepath.VolumeName(path)) + dv := strings.ToUpper(filepath.VolumeName(dir)) + path = path[len(pv):] + dir = dir[len(dv):] + switch { + default: + return false + case pv != dv: + return false + case len(path) == len(dir): + if path == dir { + return true + } + return false + case dir == "": + return path != "" + case len(path) > len(dir): + if dir[len(dir)-1] == filepath.Separator { + if path[:len(dir)] == dir { + return path[len(dir):] != "" + } + return false + } + if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir { + if len(path) == len(dir)+1 { + return true + } + return path[len(dir)+1:] != "" + } + return false + } +} + +// IsValidImport returns whether importPkgPath is importable +// by pkgPath +func IsValidImport(pkgPath, importPkgPath PackagePath) bool { + i := strings.LastIndex(string(importPkgPath), "/internal/") + if i == -1 { + return true + } + // TODO(rfindley): this looks wrong: IsCommandLineArguments is meant to + // operate on package IDs, not package paths. + if IsCommandLineArguments(PackageID(pkgPath)) { + return true + } + // TODO(rfindley): this is wrong. mod.testx/p should not be able to + // import mod.test/internal: https://go.dev/play/p/-Ca6P-E4V4q + return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i])) +} + +// IsCommandLineArguments reports whether a given value denotes +// "command-line-arguments" package, which is a package with an unknown ID +// created by the go command. It can have a test variant, which is why callers +// should not check that a value equals "command-line-arguments" directly. +func IsCommandLineArguments(id PackageID) bool { + return strings.Contains(string(id), "command-line-arguments") +} + +// RecvIdent returns the type identifier of a method receiver. +// e.g. A for all of A, *A, A[T], *A[T], etc. +func RecvIdent(recv *ast.FieldList) *ast.Ident { + if recv == nil || len(recv.List) == 0 { + return nil + } + x := recv.List[0].Type + if star, ok := x.(*ast.StarExpr); ok { + x = star.X + } + switch ix := x.(type) { // check for instantiated receivers + case *ast.IndexExpr: + x = ix.X + case *typeparams.IndexListExpr: + x = ix.X + } + if ident, ok := x.(*ast.Ident); ok { + return ident + } + return nil +} + +// embeddedIdent returns the type name identifier for an embedding x, if x in a +// valid embedding. Otherwise, it returns nil. +// +// Spec: An embedded field must be specified as a type name T or as a pointer +// to a non-interface type name *T +func embeddedIdent(x ast.Expr) *ast.Ident { + if star, ok := x.(*ast.StarExpr); ok { + x = star.X + } + switch ix := x.(type) { // check for instantiated receivers + case *ast.IndexExpr: + x = ix.X + case *typeparams.IndexListExpr: + x = ix.X + } + switch x := x.(type) { + case *ast.Ident: + return x + case *ast.SelectorExpr: + if _, ok := x.X.(*ast.Ident); ok { + return x.Sel + } + } + return nil +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/view.go b/gopls/golang_org_x_tools_gopls/lsp/source/view.go new file mode 100644 index 0000000..c34257d --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/view.go @@ -0,0 +1,715 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "bytes" + "context" + "crypto/sha256" + "errors" + "fmt" + "go/ast" + "go/scanner" + "go/token" + "go/types" + "io" + "strings" + + "golang.org/x/mod/modfile" + "golang.org/x/mod/module" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/packages" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/govulncheck" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/gocommand" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/imports" +) + +// Snapshot represents the current state for the given view. +type Snapshot interface { + ID() uint64 + + // View returns the View associated with this snapshot. + View() View + + // BackgroundContext returns a context used for all background processing + // on behalf of this snapshot. + BackgroundContext() context.Context + + // Fileset returns the Fileset used to parse all the Go files in this snapshot. + // + // If the files are known to belong to a specific Package, use + // Package.FileSet instead. (We plan to eliminate the + // Snapshot's cache of parsed files, and thus the need for a + // snapshot-wide FileSet.) + FileSet() *token.FileSet + + // ValidBuildConfiguration returns true if there is some error in the + // user's workspace. In particular, if they are both outside of a module + // and their GOPATH. + ValidBuildConfiguration() bool + + // WriteEnv writes the view-specific environment to the io.Writer. + WriteEnv(ctx context.Context, w io.Writer) error + + // FindFile returns the FileHandle for the given URI, if it is already + // in the given snapshot. + FindFile(uri span.URI) VersionedFileHandle + + // GetVersionedFile returns the VersionedFileHandle for a given URI, + // initializing it if it is not already part of the snapshot. + GetVersionedFile(ctx context.Context, uri span.URI) (VersionedFileHandle, error) + + // GetFile returns the FileHandle for a given URI, initializing it if it is + // not already part of the snapshot. + GetFile(ctx context.Context, uri span.URI) (FileHandle, error) + + // AwaitInitialized waits until the snapshot's view is initialized. + AwaitInitialized(ctx context.Context) + + // IsOpen returns whether the editor currently has a file open. + IsOpen(uri span.URI) bool + + // IgnoredFile reports if a file would be ignored by a `go list` of the whole + // workspace. + IgnoredFile(uri span.URI) bool + + // Templates returns the .tmpl files + Templates() map[span.URI]VersionedFileHandle + + // ParseGo returns the parsed AST for the file. + // If the file is not available, returns nil and an error. + ParseGo(ctx context.Context, fh FileHandle, mode ParseMode) (*ParsedGoFile, error) + + // DiagnosePackage returns basic diagnostics, including list, parse, and type errors + // for pkg, grouped by file. + DiagnosePackage(ctx context.Context, pkg Package) (map[span.URI][]*Diagnostic, error) + + // Analyze runs the analyses for the given package at this snapshot. + Analyze(ctx context.Context, pkgID PackageID, analyzers []*Analyzer) ([]*Diagnostic, error) + + // RunGoCommandPiped runs the given `go` command, writing its output + // to stdout and stderr. Verb, Args, and WorkingDir must be specified. + // + // RunGoCommandPiped runs the command serially using gocommand.RunPiped, + // enforcing that this command executes exclusively to other commands on the + // server. + RunGoCommandPiped(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error + + // RunGoCommandDirect runs the given `go` command. Verb, Args, and + // WorkingDir must be specified. + RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) + + // RunGoCommands runs a series of `go` commands that updates the go.mod + // and go.sum file for wd, and returns their updated contents. + RunGoCommands(ctx context.Context, allowNetwork bool, wd string, run func(invoke func(...string) (*bytes.Buffer, error)) error) (bool, []byte, []byte, error) + + // RunProcessEnvFunc runs fn with the process env for this snapshot's view. + // Note: the process env contains cached module and filesystem state. + RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error + + // ModFiles are the go.mod files enclosed in the snapshot's view and known + // to the snapshot. + ModFiles() []span.URI + + // ParseMod is used to parse go.mod files. + ParseMod(ctx context.Context, fh FileHandle) (*ParsedModule, error) + + // ModWhy returns the results of `go mod why` for the module specified by + // the given go.mod file. + ModWhy(ctx context.Context, fh FileHandle) (map[string]string, error) + + // ModTidy returns the results of `go mod tidy` for the module specified by + // the given go.mod file. + ModTidy(ctx context.Context, pm *ParsedModule) (*TidiedModule, error) + + // GoModForFile returns the URI of the go.mod file for the given URI. + GoModForFile(uri span.URI) span.URI + + // WorkFile, if non-empty, is the go.work file for the workspace. + WorkFile() span.URI + + // ParseWork is used to parse go.work files. + ParseWork(ctx context.Context, fh FileHandle) (*ParsedWorkFile, error) + + // BuiltinFile returns information about the special builtin package. + BuiltinFile(ctx context.Context) (*ParsedGoFile, error) + + // IsBuiltin reports whether uri is part of the builtin package. + IsBuiltin(ctx context.Context, uri span.URI) bool + + // PackagesForFile returns an unordered list of packages that contain + // the file denoted by uri, type checked in the specified mode. + // + // If withIntermediateTestVariants is set, the resulting package set includes + // intermediate test variants. + PackagesForFile(ctx context.Context, uri span.URI, mode TypecheckMode, withIntermediateTestVariants bool) ([]Package, error) + + // PackageForFile returns a single package that this file belongs to, + // checked in mode and filtered by the package policy. + PackageForFile(ctx context.Context, uri span.URI, mode TypecheckMode, selectPackage PackageFilter) (Package, error) + + // GetActiveReverseDeps returns the active files belonging to the reverse + // dependencies of this file's package, checked in TypecheckWorkspace mode. + GetReverseDependencies(ctx context.Context, id PackageID) ([]Package, error) + + // CachedImportPaths returns all the imported packages loaded in this + // snapshot, indexed by their package path (not import path, despite the name) + // and checked in TypecheckWorkspace mode. + CachedImportPaths(ctx context.Context) (map[PackagePath]Package, error) + + // KnownPackages returns all the packages loaded in this snapshot, checked + // in TypecheckWorkspace mode. + KnownPackages(ctx context.Context) ([]Package, error) + + // ActivePackages returns the packages considered 'active' in the workspace. + // + // In normal memory mode, this is all workspace packages. In degraded memory + // mode, this is just the reverse transitive closure of open packages. + ActivePackages(ctx context.Context) ([]Package, error) + + // AllValidMetadata returns all valid metadata loaded for the snapshot. + AllValidMetadata(ctx context.Context) ([]Metadata, error) + + // WorkspacePackageByID returns the workspace package with id, type checked + // in 'workspace' mode. + WorkspacePackageByID(ctx context.Context, id PackageID) (Package, error) + + // Symbols returns all symbols in the snapshot. + Symbols(ctx context.Context) map[span.URI][]Symbol + + // Metadata returns package metadata associated with the given file URI. + MetadataForFile(ctx context.Context, uri span.URI) ([]Metadata, error) + + // GetCriticalError returns any critical errors in the workspace. + GetCriticalError(ctx context.Context) *CriticalError + + // BuildGoplsMod generates a go.mod file for all modules in the workspace. + // It bypasses any existing gopls.mod. + BuildGoplsMod(ctx context.Context) (*modfile.File, error) +} + +// PackageFilter sets how a package is filtered out from a set of packages +// containing a given file. +type PackageFilter int + +const ( + // NarrowestPackage picks the "narrowest" package for a given file. + // By "narrowest" package, we mean the package with the fewest number of + // files that includes the given file. This solves the problem of test + // variants, as the test will have more files than the non-test package. + NarrowestPackage PackageFilter = iota + + // WidestPackage returns the Package containing the most files. + // This is useful for something like diagnostics, where we'd prefer to + // offer diagnostics for as many files as possible. + WidestPackage +) + +// InvocationFlags represents the settings of a particular go command invocation. +// It is a mode, plus a set of flag bits. +type InvocationFlags int + +const ( + // Normal is appropriate for commands that might be run by a user and don't + // deliberately modify go.mod files, e.g. `go test`. + Normal InvocationFlags = iota + // WriteTemporaryModFile is for commands that need information from a + // modified version of the user's go.mod file, e.g. `go mod tidy` used to + // generate diagnostics. + WriteTemporaryModFile + // LoadWorkspace is for packages.Load, and other operations that should + // consider the whole workspace at once. + LoadWorkspace + + // AllowNetwork is a flag bit that indicates the invocation should be + // allowed to access the network. + AllowNetwork InvocationFlags = 1 << 10 +) + +func (m InvocationFlags) Mode() InvocationFlags { + return m & (AllowNetwork - 1) +} + +func (m InvocationFlags) AllowNetwork() bool { + return m&AllowNetwork != 0 +} + +// View represents a single workspace. +// This is the level at which we maintain configuration like working directory +// and build tags. +type View interface { + // Name returns the name this view was constructed with. + Name() string + + // Folder returns the folder with which this view was created. + Folder() span.URI + + // Shutdown closes this view, and detaches it from its session. + Shutdown(ctx context.Context) + + // Options returns a copy of the Options for this view. + Options() *Options + + // SetOptions sets the options of this view to new values. + // Calling this may cause the view to be invalidated and a replacement view + // added to the session. If so the new view will be returned, otherwise the + // original one will be. + SetOptions(context.Context, *Options) (View, error) + + // Snapshot returns the current snapshot for the view, and a + // release function that must be called when the Snapshot is + // no longer needed. + Snapshot(ctx context.Context) (Snapshot, func()) + + // IsGoPrivatePath reports whether target is a private import path, as identified + // by the GOPRIVATE environment variable. + IsGoPrivatePath(path string) bool + + // ModuleUpgrades returns known module upgrades for the dependencies of + // modfile. + ModuleUpgrades(modfile span.URI) map[string]string + + // RegisterModuleUpgrades registers that upgrades exist for the given modules + // required by modfile. + RegisterModuleUpgrades(modfile span.URI, upgrades map[string]string) + + // ClearModuleUpgrades clears all upgrades for the modules in modfile. + ClearModuleUpgrades(modfile span.URI) + + // Vulnerabilites returns known vulnerabilities for the given modfile. + // TODO(suzmue): replace command.Vuln with a different type, maybe + // https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck/govulnchecklib#Summary? + Vulnerabilities(modfile span.URI) []*govulncheck.Vuln + + // SetVulnerabilities resets the list of vulnerabilites that exists for the given modules + // required by modfile. + SetVulnerabilities(modfile span.URI, vulnerabilities []*govulncheck.Vuln) + + // FileKind returns the type of a file + FileKind(FileHandle) FileKind + + // GoVersion returns the configured Go version for this view. + GoVersion() int +} + +// A FileSource maps uris to FileHandles. This abstraction exists both for +// testability, and so that algorithms can be run equally on session and +// snapshot files. +type FileSource interface { + // GetFile returns the FileHandle for a given URI. + GetFile(ctx context.Context, uri span.URI) (FileHandle, error) +} + +// A ParsedGoFile contains the results of parsing a Go file. +type ParsedGoFile struct { + URI span.URI + Mode ParseMode + File *ast.File + Tok *token.File + // Source code used to build the AST. It may be different from the + // actual content of the file if we have fixed the AST. + Src []byte + Fixed bool + Mapper *protocol.ColumnMapper + ParseErr scanner.ErrorList +} + +// A ParsedModule contains the results of parsing a go.mod file. +type ParsedModule struct { + URI span.URI + File *modfile.File + Mapper *protocol.ColumnMapper + ParseErrors []*Diagnostic +} + +// A ParsedWorkFile contains the results of parsing a go.work file. +type ParsedWorkFile struct { + URI span.URI + File *modfile.WorkFile + Mapper *protocol.ColumnMapper + ParseErrors []*Diagnostic +} + +// A TidiedModule contains the results of running `go mod tidy` on a module. +type TidiedModule struct { + // Diagnostics representing changes made by `go mod tidy`. + Diagnostics []*Diagnostic + // The bytes of the go.mod file after it was tidied. + TidiedContent []byte +} + +// Metadata represents package metadata retrieved from go/packages. +type Metadata interface { + // PackageID is the unique package id. + PackageID() PackageID + + // PackageName is the package name. + PackageName() PackageName + + // PackagePath is the package path. + PackagePath() PackagePath + + // ModuleInfo returns the go/packages module information for the given package. + ModuleInfo() *packages.Module +} + +var ErrViewExists = errors.New("view already exists for session") + +// Overlay is the type for a file held in memory on a session. +type Overlay interface { + Kind() FileKind + VersionedFileHandle +} + +// FileModification represents a modification to a file. +type FileModification struct { + URI span.URI + Action FileAction + + // OnDisk is true if a watched file is changed on disk. + // If true, Version will be -1 and Text will be nil. + OnDisk bool + + // Version will be -1 and Text will be nil when they are not supplied, + // specifically on textDocument/didClose and for on-disk changes. + Version int32 + Text []byte + + // LanguageID is only sent from the language client on textDocument/didOpen. + LanguageID string +} + +type FileAction int + +const ( + UnknownFileAction = FileAction(iota) + Open + Change + Close + Save + Create + Delete + InvalidateMetadata +) + +func (a FileAction) String() string { + switch a { + case Open: + return "Open" + case Change: + return "Change" + case Close: + return "Close" + case Save: + return "Save" + case Create: + return "Create" + case Delete: + return "Delete" + case InvalidateMetadata: + return "InvalidateMetadata" + default: + return "Unknown" + } +} + +var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version") +var ErrNoModOnDisk = errors.New("go.mod file is not on disk") + +func IsNonFatalGoModError(err error) bool { + return err == ErrTmpModfileUnsupported || err == ErrNoModOnDisk +} + +// ParseMode controls the content of the AST produced when parsing a source file. +type ParseMode int + +const ( + // ParseHeader specifies that the main package declaration and imports are needed. + // This is the mode used when attempting to examine the package graph structure. + ParseHeader ParseMode = iota + + // ParseExported specifies that the package is used only as a dependency, + // and only its exported declarations are needed. More may be included if + // necessary to avoid type errors. + ParseExported + + // ParseFull specifies the full AST is needed. + // This is used for files of direct interest where the entire contents must + // be considered. + ParseFull +) + +// AllParseModes contains all possible values of ParseMode. +// It is used for cache invalidation on a file content change. +var AllParseModes = []ParseMode{ParseHeader, ParseExported, ParseFull} + +// TypecheckMode controls what kind of parsing should be done (see ParseMode) +// while type checking a package. +type TypecheckMode int + +const ( + // TypecheckFull means to use ParseFull. + TypecheckFull TypecheckMode = iota + // TypecheckWorkspace means to use ParseFull for workspace packages, and + // ParseExported for others. + TypecheckWorkspace +) + +type VersionedFileHandle interface { + FileHandle + Version() int32 + Session() string + + // LSPIdentity returns the version identity of a file. + VersionedFileIdentity() VersionedFileIdentity +} + +type VersionedFileIdentity struct { + URI span.URI + + // SessionID is the ID of the LSP session. + SessionID string + + // Version is the version of the file, as specified by the client. It should + // only be set in combination with SessionID. + Version int32 +} + +// FileHandle represents a handle to a specific version of a single file. +type FileHandle interface { + URI() span.URI + + // FileIdentity returns a FileIdentity for the file, even if there was an + // error reading it. + FileIdentity() FileIdentity + // Read reads the contents of a file. + // If the file is not available, returns a nil slice and an error. + Read() ([]byte, error) + // Saved reports whether the file has the same content on disk. + Saved() bool +} + +// A Hash is a cryptographic digest of the contents of a file. +// (Although at 32B it is larger than a 16B string header, it is smaller +// and has better locality than the string header + 64B of hex digits.) +type Hash [sha256.Size]byte + +// HashOf returns the hash of some data. +func HashOf(data []byte) Hash { + return Hash(sha256.Sum256(data)) +} + +// Hashf returns the hash of a printf-formatted string. +func Hashf(format string, args ...interface{}) Hash { + // Although this looks alloc-heavy, it is faster than using + // Fprintf on sha256.New() because the allocations don't escape. + return HashOf([]byte(fmt.Sprintf(format, args...))) +} + +// String returns the digest as a string of hex digits. +func (h Hash) String() string { + return fmt.Sprintf("%64x", [sha256.Size]byte(h)) +} + +// Less returns true if the given hash is less than the other. +func (h Hash) Less(other Hash) bool { + return bytes.Compare(h[:], other[:]) < 0 +} + +// XORWith updates *h to *h XOR h2. +func (h *Hash) XORWith(h2 Hash) { + // Small enough that we don't need crypto/subtle.XORBytes. + for i := range h { + h[i] ^= h2[i] + } +} + +// FileIdentity uniquely identifies a file at a version from a FileSystem. +type FileIdentity struct { + URI span.URI + Hash Hash // digest of file contents +} + +func (id FileIdentity) String() string { + return fmt.Sprintf("%s%s", id.URI, id.Hash) +} + +// FileKind describes the kind of the file in question. +// It can be one of Go,mod, Sum, or Tmpl. +type FileKind int + +const ( + // UnknownKind is a file type we don't know about. + UnknownKind = FileKind(iota) + + // Go is a normal go source file. + Go + // Mod is a go.mod file. + Mod + // Sum is a go.sum file. + Sum + // Tmpl is a template file. + Tmpl + // Work is a go.work file. + Work +) + +// Analyzer represents a go/analysis analyzer with some boolean properties +// that let the user know how to use the analyzer. +type Analyzer struct { + Analyzer *analysis.Analyzer + + // Enabled reports whether the analyzer is enabled. This value can be + // configured per-analysis in user settings. For staticcheck analyzers, + // the value of the Staticcheck setting overrides this field. + Enabled bool + + // Fix is the name of the suggested fix name used to invoke the suggested + // fixes for the analyzer. It is non-empty if we expect this analyzer to + // provide its fix separately from its diagnostics. That is, we should apply + // the analyzer's suggested fixes through a Command, not a TextEdit. + Fix string + + // ActionKind is the kind of code action this analyzer produces. If + // unspecified the type defaults to quickfix. + ActionKind []protocol.CodeActionKind + + // Severity is the severity set for diagnostics reported by this + // analyzer. If left unset it defaults to Warning. + Severity protocol.DiagnosticSeverity +} + +func (a Analyzer) IsEnabled(view View) bool { + // Staticcheck analyzers can only be enabled when staticcheck is on. + if _, ok := view.Options().StaticcheckAnalyzers[a.Analyzer.Name]; ok { + if !view.Options().Staticcheck { + return false + } + } + if enabled, ok := view.Options().Analyses[a.Analyzer.Name]; ok { + return enabled + } + return a.Enabled +} + +// Declare explicit types for package paths, names, and IDs to ensure that we +// never use an ID where a path belongs, and vice versa. If we confused these, +// it would result in confusing errors because package IDs often look like +// package paths. +type ( + PackageID string // go list's unique identifier for a package (e.g. "vendor/example.com/foo [vendor/example.com/bar.test]") + PackagePath string // name used to prefix linker symbols (e.g. "vendor/example.com/foo") + PackageName string // identifier in 'package' declaration (e.g. "foo") + ImportPath string // path that appears in an import declaration (e.g. "example.com/foo") +) + +// Package represents a Go package that has been type-checked. It maintains +// only the relevant fields of a *go/packages.Package. +type Package interface { + ID() PackageID + Name() PackageName + PkgPath() PackagePath + CompiledGoFiles() []*ParsedGoFile + File(uri span.URI) (*ParsedGoFile, error) + FileSet() *token.FileSet + GetSyntax() []*ast.File + GetTypes() *types.Package + GetTypesInfo() *types.Info + GetTypesSizes() types.Sizes + ForTest() string + DirectDep(path PackagePath) (Package, error) + ResolveImportPath(path ImportPath) (Package, error) + MissingDependencies() []ImportPath // unordered + Imports() []Package + Version() *module.Version + HasListOrParseErrors() bool + HasTypeErrors() bool + ParseMode() ParseMode +} + +// A CriticalError is a workspace-wide error that generally prevents gopls from +// functioning correctly. In the presence of critical errors, other diagnostics +// in the workspace may not make sense. +type CriticalError struct { + // MainError is the primary error. Must be non-nil. + MainError error + + // Diagnostics contains any supplemental (structured) diagnostics. + Diagnostics []*Diagnostic +} + +// An Diagnostic corresponds to an LSP Diagnostic. +// https://microsoft.github.io/language-server-protocol/specification#diagnostic +type Diagnostic struct { + URI span.URI + Range protocol.Range + Severity protocol.DiagnosticSeverity + Code string + CodeHref string + + // Source is a human-readable description of the source of the error. + // Diagnostics generated by an analysis.Analyzer set it to Analyzer.Name. + Source DiagnosticSource + + Message string + + Tags []protocol.DiagnosticTag + Related []RelatedInformation + + // Fields below are used internally to generate quick fixes. They aren't + // part of the LSP spec and don't leave the server. + SuggestedFixes []SuggestedFix + Analyzer *Analyzer +} + +func (d *Diagnostic) String() string { + return fmt.Sprintf("%v: %s", d.Range, d.Message) +} + +type DiagnosticSource string + +const ( + UnknownError DiagnosticSource = "" + ListError DiagnosticSource = "go list" + ParseError DiagnosticSource = "syntax" + TypeError DiagnosticSource = "compiler" + ModTidyError DiagnosticSource = "go mod tidy" + OptimizationDetailsError DiagnosticSource = "optimizer details" + UpgradeNotification DiagnosticSource = "upgrade available" + Vulncheck DiagnosticSource = "govulncheck" + TemplateError DiagnosticSource = "template" + WorkFileError DiagnosticSource = "go.work file" +) + +func AnalyzerErrorKind(name string) DiagnosticSource { + return DiagnosticSource(name) +} + +// WorkspaceModuleVersion is the nonexistent pseudoversion suffix used in the +// construction of the workspace module. It is exported so that we can make +// sure not to show this version to end users in error messages, to avoid +// confusion. +// The major version is not included, as that depends on the module path. +// +// If workspace module A is dependent on workspace module B, we need our +// nonexistent version to be greater than the version A mentions. +// Otherwise, the go command will try to update to that version. Use a very +// high minor version to make that more likely. +const workspaceModuleVersion = ".9999999.0-goplsworkspace" + +func IsWorkspaceModuleVersion(version string) bool { + return strings.HasSuffix(version, workspaceModuleVersion) +} + +func WorkspaceModuleVersion(majorVersion string) string { + // Use the highest compatible major version to avoid unwanted upgrades. + // See the comment on workspaceModuleVersion. + if majorVersion == "v0" { + majorVersion = "v1" + } + return majorVersion + workspaceModuleVersion +} diff --git a/gopls/golang_org_x_tools_gopls/lsp/source/workspace_symbol.go b/gopls/golang_org_x_tools_gopls/lsp/source/workspace_symbol.go new file mode 100644 index 0000000..8faca46 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/lsp/source/workspace_symbol.go @@ -0,0 +1,626 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "context" + "fmt" + "go/types" + "path" + "path/filepath" + "regexp" + "runtime" + "sort" + "strings" + "unicode" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/span" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/event" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/fuzzy" +) + +// Symbol holds a precomputed symbol value. Note: we avoid using the +// protocol.SymbolInformation struct here in order to reduce the size of each +// symbol. +type Symbol struct { + Name string + Kind protocol.SymbolKind + Range protocol.Range +} + +// maxSymbols defines the maximum number of symbol results that should ever be +// sent in response to a client. +const maxSymbols = 100 + +// WorkspaceSymbols matches symbols across all views using the given query, +// according to the match semantics parameterized by matcherType and style. +// +// The workspace symbol method is defined in the spec as follows: +// +// The workspace symbol request is sent from the client to the server to +// list project-wide symbols matching the query string. +// +// It is unclear what "project-wide" means here, but given the parameters of +// workspace/symbol do not include any workspace identifier, then it has to be +// assumed that "project-wide" means "across all workspaces". Hence why +// WorkspaceSymbols receives the views []View. +// +// However, it then becomes unclear what it would mean to call WorkspaceSymbols +// with a different configured SymbolMatcher per View. Therefore we assume that +// Session level configuration will define the SymbolMatcher to be used for the +// WorkspaceSymbols method. +func WorkspaceSymbols(ctx context.Context, matcher SymbolMatcher, style SymbolStyle, views []View, query string) ([]protocol.SymbolInformation, error) { + ctx, done := event.Start(ctx, "source.WorkspaceSymbols") + defer done() + if query == "" { + return nil, nil + } + + var s symbolizer + switch style { + case DynamicSymbols: + s = dynamicSymbolMatch + case FullyQualifiedSymbols: + s = fullyQualifiedSymbolMatch + case PackageQualifiedSymbols: + s = packageSymbolMatch + default: + panic(fmt.Errorf("unknown symbol style: %v", style)) + } + + return collectSymbols(ctx, views, matcher, s, query) +} + +// A matcherFunc returns the index and score of a symbol match. +// +// See the comment for symbolCollector for more information. +type matcherFunc func(chunks []string) (int, float64) + +// A symbolizer returns the best symbol match for a name with pkg, according to +// some heuristic. The symbol name is passed as the slice nameParts of logical +// name pieces. For example, for myType.field the caller can pass either +// []string{"myType.field"} or []string{"myType.", "field"}. +// +// See the comment for symbolCollector for more information. +// +// The space argument is an empty slice with spare capacity that may be used +// to allocate the result. +type symbolizer func(space []string, name string, pkg Metadata, m matcherFunc) ([]string, float64) + +func fullyQualifiedSymbolMatch(space []string, name string, pkg Metadata, matcher matcherFunc) ([]string, float64) { + if _, score := dynamicSymbolMatch(space, name, pkg, matcher); score > 0 { + return append(space, string(pkg.PackagePath()), ".", name), score + } + return nil, 0 +} + +func dynamicSymbolMatch(space []string, name string, pkg Metadata, matcher matcherFunc) ([]string, float64) { + if IsCommandLineArguments(pkg.PackageID()) { + // command-line-arguments packages have a non-sensical package path, so + // just use their package name. + return packageSymbolMatch(space, name, pkg, matcher) + } + + var score float64 + + endsInPkgName := strings.HasSuffix(string(pkg.PackagePath()), string(pkg.PackageName())) + + // If the package path does not end in the package name, we need to check the + // package-qualified symbol as an extra pass first. + if !endsInPkgName { + pkgQualified := append(space, string(pkg.PackageName()), ".", name) + idx, score := matcher(pkgQualified) + nameStart := len(pkg.PackageName()) + 1 + if score > 0 { + // If our match is contained entirely within the unqualified portion, + // just return that. + if idx >= nameStart { + return append(space, name), score + } + // Lower the score for matches that include the package name. + return pkgQualified, score * 0.8 + } + } + + // Now try matching the fully qualified symbol. + fullyQualified := append(space, string(pkg.PackagePath()), ".", name) + idx, score := matcher(fullyQualified) + + // As above, check if we matched just the unqualified symbol name. + nameStart := len(pkg.PackagePath()) + 1 + if idx >= nameStart { + return append(space, name), score + } + + // If our package path ends in the package name, we'll have skipped the + // initial pass above, so check if we matched just the package-qualified + // name. + if endsInPkgName && idx >= 0 { + pkgStart := len(pkg.PackagePath()) - len(pkg.PackageName()) + if idx >= pkgStart { + return append(space, string(pkg.PackageName()), ".", name), score + } + } + + // Our match was not contained within the unqualified or package qualified + // symbol. Return the fully qualified symbol but discount the score. + return fullyQualified, score * 0.6 +} + +func packageSymbolMatch(space []string, name string, pkg Metadata, matcher matcherFunc) ([]string, float64) { + qualified := append(space, string(pkg.PackageName()), ".", name) + if _, s := matcher(qualified); s > 0 { + return qualified, s + } + return nil, 0 +} + +func buildMatcher(matcher SymbolMatcher, query string) matcherFunc { + switch matcher { + case SymbolFuzzy: + return parseQuery(query, newFuzzyMatcher) + case SymbolFastFuzzy: + return parseQuery(query, func(query string) matcherFunc { + return fuzzy.NewSymbolMatcher(query).Match + }) + case SymbolCaseSensitive: + return matchExact(query) + case SymbolCaseInsensitive: + q := strings.ToLower(query) + exact := matchExact(q) + wrapper := []string{""} + return func(chunks []string) (int, float64) { + s := strings.Join(chunks, "") + wrapper[0] = strings.ToLower(s) + return exact(wrapper) + } + } + panic(fmt.Errorf("unknown symbol matcher: %v", matcher)) +} + +func newFuzzyMatcher(query string) matcherFunc { + fm := fuzzy.NewMatcher(query) + return func(chunks []string) (int, float64) { + score := float64(fm.ScoreChunks(chunks)) + ranges := fm.MatchedRanges() + if len(ranges) > 0 { + return ranges[0], score + } + return -1, score + } +} + +// parseQuery parses a field-separated symbol query, extracting the special +// characters listed below, and returns a matcherFunc corresponding to the AND +// of all field queries. +// +// Special characters: +// +// ^ match exact prefix +// $ match exact suffix +// ' match exact +// +// In all three of these special queries, matches are 'smart-cased', meaning +// they are case sensitive if the symbol query contains any upper-case +// characters, and case insensitive otherwise. +func parseQuery(q string, newMatcher func(string) matcherFunc) matcherFunc { + fields := strings.Fields(q) + if len(fields) == 0 { + return func([]string) (int, float64) { return -1, 0 } + } + var funcs []matcherFunc + for _, field := range fields { + var f matcherFunc + switch { + case strings.HasPrefix(field, "^"): + prefix := field[1:] + f = smartCase(prefix, func(chunks []string) (int, float64) { + s := strings.Join(chunks, "") + if strings.HasPrefix(s, prefix) { + return 0, 1 + } + return -1, 0 + }) + case strings.HasPrefix(field, "'"): + exact := field[1:] + f = smartCase(exact, matchExact(exact)) + case strings.HasSuffix(field, "$"): + suffix := field[0 : len(field)-1] + f = smartCase(suffix, func(chunks []string) (int, float64) { + s := strings.Join(chunks, "") + if strings.HasSuffix(s, suffix) { + return len(s) - len(suffix), 1 + } + return -1, 0 + }) + default: + f = newMatcher(field) + } + funcs = append(funcs, f) + } + if len(funcs) == 1 { + return funcs[0] + } + return comboMatcher(funcs).match +} + +func matchExact(exact string) matcherFunc { + return func(chunks []string) (int, float64) { + s := strings.Join(chunks, "") + if idx := strings.LastIndex(s, exact); idx >= 0 { + return idx, 1 + } + return -1, 0 + } +} + +// smartCase returns a matcherFunc that is case-sensitive if q contains any +// upper-case characters, and case-insensitive otherwise. +func smartCase(q string, m matcherFunc) matcherFunc { + insensitive := strings.ToLower(q) == q + wrapper := []string{""} + return func(chunks []string) (int, float64) { + s := strings.Join(chunks, "") + if insensitive { + s = strings.ToLower(s) + } + wrapper[0] = s + return m(wrapper) + } +} + +type comboMatcher []matcherFunc + +func (c comboMatcher) match(chunks []string) (int, float64) { + score := 1.0 + first := 0 + for _, f := range c { + idx, s := f(chunks) + if idx < first { + first = idx + } + score *= s + } + return first, score +} + +// collectSymbols calls snapshot.Symbols to walk the syntax trees of +// all files in the views' current snapshots, and returns a sorted, +// scored list of symbols that best match the parameters. +// +// How it matches symbols is parameterized by two interfaces: +// - A matcherFunc determines how well a string symbol matches a query. It +// returns a non-negative score indicating the quality of the match. A score +// of zero indicates no match. +// - A symbolizer determines how we extract the symbol for an object. This +// enables the 'symbolStyle' configuration option. +func collectSymbols(ctx context.Context, views []View, matcherType SymbolMatcher, symbolizer symbolizer, query string) ([]protocol.SymbolInformation, error) { + + // Extract symbols from all files. + var work []symbolFile + var roots []string + seen := make(map[span.URI]bool) + // TODO(adonovan): opt: parallelize this loop? How often is len > 1? + for _, v := range views { + snapshot, release := v.Snapshot(ctx) + defer release() + + // Use the root view URIs for determining (lexically) + // whether a URI is in any open workspace. + roots = append(roots, strings.TrimRight(string(v.Folder()), "/")) + + filters := v.Options().DirectoryFilters + filterer := NewFilterer(filters) + folder := filepath.ToSlash(v.Folder().Filename()) + for uri, syms := range snapshot.Symbols(ctx) { + norm := filepath.ToSlash(uri.Filename()) + nm := strings.TrimPrefix(norm, folder) + if filterer.Disallow(nm) { + continue + } + // Only scan each file once. + if seen[uri] { + continue + } + mds, err := snapshot.MetadataForFile(ctx, uri) + if err != nil { + event.Error(ctx, fmt.Sprintf("missing metadata for %q", uri), err) + continue + } + if len(mds) == 0 { + // TODO: should use the bug reporting API + continue + } + seen[uri] = true + work = append(work, symbolFile{uri, mds[0], syms}) + } + } + + // Match symbols in parallel. + // Each worker has its own symbolStore, + // which we merge at the end. + nmatchers := runtime.GOMAXPROCS(-1) // matching is CPU bound + results := make(chan *symbolStore) + for i := 0; i < nmatchers; i++ { + go func(i int) { + matcher := buildMatcher(matcherType, query) + store := new(symbolStore) + // Assign files to workers in round-robin fashion. + for j := i; j < len(work); j += nmatchers { + matchFile(store, symbolizer, matcher, roots, work[j]) + } + results <- store + }(i) + } + + // Gather and merge results as they arrive. + var unified symbolStore + for i := 0; i < nmatchers; i++ { + store := <-results + for _, syms := range store.res { + unified.store(syms) + } + } + return unified.results(), nil +} + +type Filterer struct { + // Whether a filter is excluded depends on the operator (first char of the raw filter). + // Slices filters and excluded then should have the same length. + filters []*regexp.Regexp + excluded []bool +} + +// NewFilterer computes regular expression form of all raw filters +func NewFilterer(rawFilters []string) *Filterer { + var f Filterer + for _, filter := range rawFilters { + filter = path.Clean(filepath.ToSlash(filter)) + // TODO(dungtuanle): fix: validate [+-] prefix. + op, prefix := filter[0], filter[1:] + // convertFilterToRegexp adds "/" at the end of prefix to handle cases where a filter is a prefix of another filter. + // For example, it prevents [+foobar, -foo] from excluding "foobar". + f.filters = append(f.filters, convertFilterToRegexp(filepath.ToSlash(prefix))) + f.excluded = append(f.excluded, op == '-') + } + + return &f +} + +// Disallow return true if the path is excluded from the filterer's filters. +func (f *Filterer) Disallow(path string) bool { + // Ensure trailing but not leading slash. + path = strings.TrimPrefix(path, "/") + if !strings.HasSuffix(path, "/") { + path += "/" + } + + // TODO(adonovan): opt: iterate in reverse and break at first match. + excluded := false + for i, filter := range f.filters { + if filter.MatchString(path) { + excluded = f.excluded[i] // last match wins + } + } + return excluded +} + +// convertFilterToRegexp replaces glob-like operator substrings in a string file path to their equivalent regex forms. +// Supporting glob-like operators: +// - **: match zero or more complete path segments +func convertFilterToRegexp(filter string) *regexp.Regexp { + if filter == "" { + return regexp.MustCompile(".*") + } + var ret strings.Builder + ret.WriteString("^") + segs := strings.Split(filter, "/") + for _, seg := range segs { + // Inv: seg != "" since path is clean. + if seg == "**" { + ret.WriteString(".*") + } else { + ret.WriteString(regexp.QuoteMeta(seg)) + } + ret.WriteString("/") + } + pattern := ret.String() + + // Remove unnecessary "^.*" prefix, which increased + // BenchmarkWorkspaceSymbols time by ~20% (even though + // filter CPU time increased by only by ~2.5%) when the + // default filter was changed to "**/node_modules". + pattern = strings.TrimPrefix(pattern, "^.*") + + return regexp.MustCompile(pattern) +} + +// symbolFile holds symbol information for a single file. +type symbolFile struct { + uri span.URI + md Metadata + syms []Symbol +} + +// matchFile scans a symbol file and adds matching symbols to the store. +func matchFile(store *symbolStore, symbolizer symbolizer, matcher matcherFunc, roots []string, i symbolFile) { + space := make([]string, 0, 3) + for _, sym := range i.syms { + symbolParts, score := symbolizer(space, sym.Name, i.md, matcher) + + // Check if the score is too low before applying any downranking. + if store.tooLow(score) { + continue + } + + // Factors to apply to the match score for the purpose of downranking + // results. + // + // These numbers were crudely calibrated based on trial-and-error using a + // small number of sample queries. Adjust as necessary. + // + // All factors are multiplicative, meaning if more than one applies they are + // multiplied together. + const ( + // nonWorkspaceFactor is applied to symbols outside of any active + // workspace. Developers are less likely to want to jump to code that they + // are not actively working on. + nonWorkspaceFactor = 0.5 + // nonWorkspaceUnexportedFactor is applied to unexported symbols outside of + // any active workspace. Since one wouldn't usually jump to unexported + // symbols to understand a package API, they are particularly irrelevant. + nonWorkspaceUnexportedFactor = 0.5 + // every field or method nesting level to access the field decreases + // the score by a factor of 1.0 - depth*depthFactor, up to a depth of + // 3. + depthFactor = 0.2 + ) + + startWord := true + exported := true + depth := 0.0 + for _, r := range sym.Name { + if startWord && !unicode.IsUpper(r) { + exported = false + } + if r == '.' { + startWord = true + depth++ + } else { + startWord = false + } + } + + inWorkspace := false + for _, root := range roots { + if strings.HasPrefix(string(i.uri), root) { + inWorkspace = true + break + } + } + + // Apply downranking based on workspace position. + if !inWorkspace { + score *= nonWorkspaceFactor + if !exported { + score *= nonWorkspaceUnexportedFactor + } + } + + // Apply downranking based on symbol depth. + if depth > 3 { + depth = 3 + } + score *= 1.0 - depth*depthFactor + + if store.tooLow(score) { + continue + } + + si := symbolInformation{ + score: score, + symbol: strings.Join(symbolParts, ""), + kind: sym.Kind, + uri: i.uri, + rng: sym.Range, + container: string(i.md.PackagePath()), + } + store.store(si) + } +} + +type symbolStore struct { + res [maxSymbols]symbolInformation +} + +// store inserts si into the sorted results, if si has a high enough score. +func (sc *symbolStore) store(si symbolInformation) { + if sc.tooLow(si.score) { + return + } + insertAt := sort.Search(len(sc.res), func(i int) bool { + // Sort by score, then symbol length, and finally lexically. + if sc.res[i].score != si.score { + return sc.res[i].score < si.score + } + if len(sc.res[i].symbol) != len(si.symbol) { + return len(sc.res[i].symbol) > len(si.symbol) + } + return sc.res[i].symbol > si.symbol + }) + if insertAt < len(sc.res)-1 { + copy(sc.res[insertAt+1:], sc.res[insertAt:len(sc.res)-1]) + } + sc.res[insertAt] = si +} + +func (sc *symbolStore) tooLow(score float64) bool { + return score <= sc.res[len(sc.res)-1].score +} + +func (sc *symbolStore) results() []protocol.SymbolInformation { + var res []protocol.SymbolInformation + for _, si := range sc.res { + if si.score <= 0 { + return res + } + res = append(res, si.asProtocolSymbolInformation()) + } + return res +} + +func typeToKind(typ types.Type) protocol.SymbolKind { + switch typ := typ.Underlying().(type) { + case *types.Interface: + return protocol.Interface + case *types.Struct: + return protocol.Struct + case *types.Signature: + if typ.Recv() != nil { + return protocol.Method + } + return protocol.Function + case *types.Named: + return typeToKind(typ.Underlying()) + case *types.Basic: + i := typ.Info() + switch { + case i&types.IsNumeric != 0: + return protocol.Number + case i&types.IsBoolean != 0: + return protocol.Boolean + case i&types.IsString != 0: + return protocol.String + } + } + return protocol.Variable +} + +// symbolInformation is a cut-down version of protocol.SymbolInformation that +// allows struct values of this type to be used as map keys. +type symbolInformation struct { + score float64 + symbol string + container string + kind protocol.SymbolKind + uri span.URI + rng protocol.Range +} + +// asProtocolSymbolInformation converts s to a protocol.SymbolInformation value. +// +// TODO: work out how to handle tags if/when they are needed. +func (s symbolInformation) asProtocolSymbolInformation() protocol.SymbolInformation { + return protocol.SymbolInformation{ + Name: s.symbol, + Kind: s.kind, + Location: protocol.Location{ + URI: protocol.URIFromSpanURI(s.uri), + Range: s.rng, + }, + ContainerName: s.container, + } +} diff --git a/gopls/golang_org_x_tools_gopls/span/parse.go b/gopls/golang_org_x_tools_gopls/span/parse.go new file mode 100644 index 0000000..c4cec16 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/span/parse.go @@ -0,0 +1,112 @@ +// 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 span + +import ( + "path/filepath" + "strconv" + "strings" + "unicode/utf8" +) + +// Parse returns the location represented by the input. +// Only file paths are accepted, not URIs. +// The returned span will be normalized, and thus if printed may produce a +// different string. +func Parse(input string) Span { + return ParseInDir(input, ".") +} + +// ParseInDir is like Parse, but interprets paths relative to wd. +func ParseInDir(input, wd string) Span { + uri := func(path string) URI { + if !filepath.IsAbs(path) { + path = filepath.Join(wd, path) + } + return URIFromPath(path) + } + // :0:0#0-0:0#0 + valid := input + var hold, offset int + hadCol := false + suf := rstripSuffix(input) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep == ":" { + valid = suf.remains + hold = suf.num + hadCol = true + suf = rstripSuffix(suf.remains) + } + switch { + case suf.sep == ":": + return New(uri(suf.remains), NewPoint(suf.num, hold, offset), Point{}) + case suf.sep == "-": + // we have a span, fall out of the case to continue + default: + // separator not valid, rewind to either the : or the start + return New(uri(valid), NewPoint(hold, 0, offset), Point{}) + } + // only the span form can get here + // at this point we still don't know what the numbers we have mean + // if have not yet seen a : then we might have either a line or a column depending + // on whether start has a column or not + // we build an end point and will fix it later if needed + end := NewPoint(suf.num, hold, offset) + hold, offset = 0, 0 + suf = rstripSuffix(suf.remains) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep != ":" { + // turns out we don't have a span after all, rewind + return New(uri(valid), end, Point{}) + } + valid = suf.remains + hold = suf.num + suf = rstripSuffix(suf.remains) + if suf.sep != ":" { + // line#offset only + return New(uri(valid), NewPoint(hold, 0, offset), end) + } + // we have a column, so if end only had one number, it is also the column + if !hadCol { + end = NewPoint(suf.num, end.v.Line, end.v.Offset) + } + return New(uri(suf.remains), NewPoint(suf.num, hold, offset), end) +} + +type suffix struct { + remains string + sep string + num int +} + +func rstripSuffix(input string) suffix { + if len(input) == 0 { + return suffix{"", "", -1} + } + remains := input + num := -1 + // first see if we have a number at the end + last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) + if last >= 0 && last < len(remains)-1 { + number, err := strconv.ParseInt(remains[last+1:], 10, 64) + if err == nil { + num = int(number) + remains = remains[:last+1] + } + } + // now see if we have a trailing separator + r, w := utf8.DecodeLastRuneInString(remains) + if r != ':' && r != '#' && r == '#' { + return suffix{input, "", -1} + } + remains = remains[:len(remains)-w] + return suffix{remains, string(r), num} +} diff --git a/gopls/golang_org_x_tools_gopls/span/span.go b/gopls/golang_org_x_tools_gopls/span/span.go new file mode 100644 index 0000000..333048a --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/span/span.go @@ -0,0 +1,288 @@ +// 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 span contains support for representing with positions and ranges in +// text files. +package span + +import ( + "encoding/json" + "fmt" + "go/token" + "path" +) + +// Span represents a source code range in standardized form. +type Span struct { + v span +} + +// Point represents a single point within a file. +// In general this should only be used as part of a Span, as on its own it +// does not carry enough information. +type Point struct { + v point +} + +type span struct { + URI URI `json:"uri"` + Start point `json:"start"` + End point `json:"end"` +} + +type point struct { + Line int `json:"line"` + Column int `json:"column"` + Offset int `json:"offset"` +} + +// Invalid is a span that reports false from IsValid +var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}} + +var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}} + +func New(uri URI, start, end Point) Span { + s := Span{v: span{URI: uri, Start: start.v, End: end.v}} + s.v.clean() + return s +} + +func NewPoint(line, col, offset int) Point { + p := Point{v: point{Line: line, Column: col, Offset: offset}} + p.v.clean() + return p +} + +func Compare(a, b Span) int { + if r := CompareURI(a.URI(), b.URI()); r != 0 { + return r + } + if r := comparePoint(a.v.Start, b.v.Start); r != 0 { + return r + } + return comparePoint(a.v.End, b.v.End) +} + +func ComparePoint(a, b Point) int { + return comparePoint(a.v, b.v) +} + +func comparePoint(a, b point) int { + if !a.hasPosition() { + if a.Offset < b.Offset { + return -1 + } + if a.Offset > b.Offset { + return 1 + } + return 0 + } + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Column < b.Column { + return -1 + } + if a.Column > b.Column { + return 1 + } + return 0 +} + +func (s Span) HasPosition() bool { return s.v.Start.hasPosition() } +func (s Span) HasOffset() bool { return s.v.Start.hasOffset() } +func (s Span) IsValid() bool { return s.v.Start.isValid() } +func (s Span) IsPoint() bool { return s.v.Start == s.v.End } +func (s Span) URI() URI { return s.v.URI } +func (s Span) Start() Point { return Point{s.v.Start} } +func (s Span) End() Point { return Point{s.v.End} } +func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) } +func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) } + +func (p Point) HasPosition() bool { return p.v.hasPosition() } +func (p Point) HasOffset() bool { return p.v.hasOffset() } +func (p Point) IsValid() bool { return p.v.isValid() } +func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) } +func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) } +func (p Point) Line() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Line +} +func (p Point) Column() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Column +} +func (p Point) Offset() int { + if !p.v.hasOffset() { + panic(fmt.Errorf("offset not set in %v", p.v)) + } + return p.v.Offset +} + +func (p point) hasPosition() bool { return p.Line > 0 } +func (p point) hasOffset() bool { return p.Offset >= 0 } +func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() } +func (p point) isZero() bool { + return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0) +} + +func (s *span) clean() { + //this presumes the points are already clean + if !s.End.isValid() || (s.End == point{}) { + s.End = s.Start + } +} + +func (p *point) clean() { + if p.Line < 0 { + p.Line = 0 + } + if p.Column <= 0 { + if p.Line > 0 { + p.Column = 1 + } else { + p.Column = 0 + } + } + if p.Offset == 0 && (p.Line > 1 || p.Column > 1) { + p.Offset = -1 + } +} + +// Format implements fmt.Formatter to print the Location in a standard form. +// The format produced is one that can be read back in using Parse. +func (s Span) Format(f fmt.State, c rune) { + fullForm := f.Flag('+') + preferOffset := f.Flag('#') + // we should always have a uri, simplify if it is file format + //TODO: make sure the end of the uri is unambiguous + uri := string(s.v.URI) + if c == 'f' { + uri = path.Base(uri) + } else if !fullForm { + uri = s.v.URI.Filename() + } + fmt.Fprint(f, uri) + if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) { + return + } + // see which bits of start to write + printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition()) + printLine := s.HasPosition() && (fullForm || !printOffset) + printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1)) + fmt.Fprint(f, ":") + if printLine { + fmt.Fprintf(f, "%d", s.v.Start.Line) + } + if printColumn { + fmt.Fprintf(f, ":%d", s.v.Start.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.Start.Offset) + } + // start is written, do we need end? + if s.IsPoint() { + return + } + // we don't print the line if it did not change + printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line) + fmt.Fprint(f, "-") + if printLine { + fmt.Fprintf(f, "%d", s.v.End.Line) + } + if printColumn { + if printLine { + fmt.Fprint(f, ":") + } + fmt.Fprintf(f, "%d", s.v.End.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.End.Offset) + } +} + +// (Currently unused, but we gain little yet by deleting it.) +func (s Span) withPosition(tf *token.File) (Span, error) { + if err := s.update(tf, true, false); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithOffset(tf *token.File) (Span, error) { + if err := s.update(tf, false, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithAll(tf *token.File) (Span, error) { + if err := s.update(tf, true, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s *Span) update(tf *token.File, withPos, withOffset bool) error { + if !s.IsValid() { + return fmt.Errorf("cannot add information to an invalid span") + } + if withPos && !s.HasPosition() { + if err := s.v.Start.updatePosition(tf); err != nil { + return err + } + if s.v.End.Offset == s.v.Start.Offset { + s.v.End = s.v.Start + } else if err := s.v.End.updatePosition(tf); err != nil { + return err + } + } + if withOffset && (!s.HasOffset() || (s.v.End.hasPosition() && !s.v.End.hasOffset())) { + if err := s.v.Start.updateOffset(tf); err != nil { + return err + } + if s.v.End.Line == s.v.Start.Line && s.v.End.Column == s.v.Start.Column { + s.v.End.Offset = s.v.Start.Offset + } else if err := s.v.End.updateOffset(tf); err != nil { + return err + } + } + return nil +} + +func (p *point) updatePosition(tf *token.File) error { + line, col, err := ToPosition(tf, p.Offset) + if err != nil { + return err + } + p.Line = line + p.Column = col + return nil +} + +func (p *point) updateOffset(tf *token.File) error { + offset, err := ToOffset(tf, p.Line, p.Column) + if err != nil { + return err + } + p.Offset = offset + return nil +} + +// SetRange implements packagestest.rangeSetter, allowing +// gopls' test suites to use Spans instead of Range in parameters. +func (span *Span) SetRange(file *token.File, start, end token.Pos) { + point := func(pos token.Pos) Point { + posn := file.Position(pos) + return NewPoint(posn.Line, posn.Column, posn.Offset) + } + *span = New(URIFromPath(file.Name()), point(start), point(end)) +} diff --git a/gopls/golang_org_x_tools_gopls/span/token.go b/gopls/golang_org_x_tools_gopls/span/token.go new file mode 100644 index 0000000..de82c91 --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/span/token.go @@ -0,0 +1,229 @@ +// 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 span + +import ( + "fmt" + "go/token" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/bug" +) + +// Range represents a source code range in token.Pos form. +// It also carries the token.File that produced the positions, so that it is +// self contained. +type Range struct { + TokFile *token.File // non-nil + Start, End token.Pos // both IsValid() +} + +// NewRange creates a new Range from a token.File and two positions within it. +// The given start position must be valid; if end is invalid, start is used as +// the end position. +// +// (If you only have a token.FileSet, use file = fset.File(start). But +// most callers know exactly which token.File they're dealing with and +// should pass it explicitly. Not only does this save a lookup, but it +// brings us a step closer to eliminating the global FileSet.) +func NewRange(file *token.File, start, end token.Pos) Range { + if file == nil { + panic("nil *token.File") + } + if !start.IsValid() { + panic("invalid start token.Pos") + } + if !end.IsValid() { + end = start + } + + // TODO(adonovan): ideally we would make this stronger assertion: + // + // // Assert that file is non-nil and contains start and end. + // _ = file.Offset(start) + // _ = file.Offset(end) + // + // but some callers (e.g. packageCompletionSurrounding, + // posToMappedRange) don't ensure this precondition. + + return Range{ + TokFile: file, + Start: start, + End: end, + } +} + +// NewTokenFile returns a token.File for the given file content. +func NewTokenFile(filename string, content []byte) *token.File { + fset := token.NewFileSet() + f := fset.AddFile(filename, -1, len(content)) + f.SetLinesForContent(content) + return f +} + +// IsPoint returns true if the range represents a single point. +func (r Range) IsPoint() bool { + return r.Start == r.End +} + +// Span converts a Range to a Span that represents the Range. +// It will fill in all the members of the Span, calculating the line and column +// information. +func (r Range) Span() (Span, error) { + return FileSpan(r.TokFile, r.TokFile, r.Start, r.End) +} + +// FileSpan returns a span within the file referenced by start and end, using a +// token.File to translate between offsets and positions. +// +// The start and end position must be contained within posFile, though due to +// line directives they may reference positions in another file. If srcFile is +// provided, it is used to map the line:column positions referenced by start +// and end to offsets in the corresponding file. +// +// TODO(adonovan): clarify whether it is valid to pass posFile==srcFile when +// //line directives are in use. If so, fix this function; if not, fix Range.Span. +func FileSpan(posFile, srcFile *token.File, start, end token.Pos) (Span, error) { + if !start.IsValid() { + return Span{}, fmt.Errorf("start pos is not valid") + } + if posFile == nil { + return Span{}, bug.Errorf("missing file association") // should never get here with a nil file + } + var s Span + var err error + var startFilename string + startFilename, s.v.Start.Line, s.v.Start.Column, err = position(posFile, start) + if err != nil { + return Span{}, err + } + s.v.URI = URIFromPath(startFilename) + if end.IsValid() { + var endFilename string + endFilename, s.v.End.Line, s.v.End.Column, err = position(posFile, end) + if err != nil { + return Span{}, err + } + // In the presence of line directives, a single File can have sections from + // multiple file names. + if endFilename != startFilename { + return Span{}, fmt.Errorf("span begins in file %q but ends in %q", startFilename, endFilename) + } + } + s.v.Start.clean() + s.v.End.clean() + s.v.clean() + tf := posFile + if srcFile != nil { + tf = srcFile + } + if startFilename != tf.Name() { + // 'start' identifies a position specified by a //line directive + // in a file other than the one containing the directive. + // (Debugging support for https://github.com/golang/go/issues/54655.) + // + // This used to be a bug.Errorf, but that was unsound because + // Range.Span passes this function the same TokFile argument twice, + // which is never going to pass this test for a file containing + // a //line directive. + // TODO(adonovan): decide where the bug.Errorf really belongs. + return Span{}, fmt.Errorf("must supply Converter for file %q (tf.Name() = %q)", startFilename, tf.Name()) + } + return s.WithOffset(tf) +} + +func position(tf *token.File, pos token.Pos) (string, int, int, error) { + off, err := offset(tf, pos) + if err != nil { + return "", 0, 0, err + } + return positionFromOffset(tf, off) +} + +func positionFromOffset(tf *token.File, offset int) (string, int, int, error) { + if offset > tf.Size() { + return "", 0, 0, fmt.Errorf("offset %d is beyond EOF (%d) in file %s", offset, tf.Size(), tf.Name()) + } + pos := tf.Pos(offset) + p := tf.Position(pos) + // TODO(golang/go#41029): Consider returning line, column instead of line+1, 1 if + // the file's last character is not a newline. + if offset == tf.Size() { + return p.Filename, p.Line + 1, 1, nil + } + return p.Filename, p.Line, p.Column, nil +} + +// offset is a copy of the Offset function in go/token, but with the adjustment +// that it does not panic on invalid positions. +func offset(tf *token.File, pos token.Pos) (int, error) { + if int(pos) < tf.Base() || int(pos) > tf.Base()+tf.Size() { + return 0, fmt.Errorf("invalid pos: %d not in [%d, %d]", pos, tf.Base(), tf.Base()+tf.Size()) + } + return int(pos) - tf.Base(), nil +} + +// Range converts a Span to a Range that represents the Span for the supplied +// File. +func (s Span) Range(tf *token.File) (Range, error) { + s, err := s.WithOffset(tf) + if err != nil { + return Range{}, err + } + // go/token will panic if the offset is larger than the file's size, + // so check here to avoid panicking. + if s.Start().Offset() > tf.Size() { + return Range{}, bug.Errorf("start offset %v is past the end of the file %v", s.Start(), tf.Size()) + } + if s.End().Offset() > tf.Size() { + return Range{}, bug.Errorf("end offset %v is past the end of the file %v", s.End(), tf.Size()) + } + return Range{ + Start: tf.Pos(s.Start().Offset()), + End: tf.Pos(s.End().Offset()), + TokFile: tf, + }, nil +} + +// ToPosition converts a byte offset in the file corresponding to tf into +// 1-based line and utf-8 column indexes. +func ToPosition(tf *token.File, offset int) (int, int, error) { + _, line, col, err := positionFromOffset(tf, offset) + return line, col, err +} + +// ToOffset converts a 1-based line and utf-8 column index into a byte offset +// in the file corresponding to tf. +func ToOffset(tf *token.File, line, col int) (int, error) { + if line < 1 { // token.File.LineStart panics if line < 1 + return -1, fmt.Errorf("invalid line: %d", line) + } + + lineMax := tf.LineCount() + 1 + if line > lineMax { + return -1, fmt.Errorf("line %d is beyond end of file %v", line, lineMax) + } else if line == lineMax { + if col > 1 { + return -1, fmt.Errorf("column is beyond end of file") + } + // at the end of the file, allowing for a trailing eol + return tf.Size(), nil + } + pos := tf.LineStart(line) + if !pos.IsValid() { + // bug.Errorf here because LineStart panics on out-of-bound input, and so + // should never return invalid positions. + return -1, bug.Errorf("line is not in file") + } + // we assume that column is in bytes here, and that the first byte of a + // line is at column 1 + pos += token.Pos(col - 1) + + // Debugging support for https://github.com/golang/go/issues/54655. + if pos > token.Pos(tf.Base()+tf.Size()) { + return 0, fmt.Errorf("ToOffset: column %d is beyond end of file", col) + } + + return offset(tf, pos) +} diff --git a/gopls/golang_org_x_tools_gopls/span/uri.go b/gopls/golang_org_x_tools_gopls/span/uri.go new file mode 100644 index 0000000..adcc54a --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/span/uri.go @@ -0,0 +1,196 @@ +// 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 span + +import ( + "fmt" + "net/url" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "unicode" +) + +const fileScheme = "file" + +// URI represents the full URI for a file. +type URI string + +func (uri URI) IsFile() bool { + return strings.HasPrefix(string(uri), "file://") +} + +// Filename returns the file path for the given URI. +// It is an error to call this on a URI that is not a valid filename. +func (uri URI) Filename() string { + filename, err := filename(uri) + if err != nil { + panic(err) + } + return filepath.FromSlash(filename) +} + +func filename(uri URI) (string, error) { + if uri == "" { + return "", nil + } + + // This conservative check for the common case + // of a simple non-empty absolute POSIX filename + // avoids the allocation of a net.URL. + if strings.HasPrefix(string(uri), "file:///") { + rest := string(uri)[len("file://"):] // leave one slash + for i := 0; i < len(rest); i++ { + b := rest[i] + // Reject these cases: + if b < ' ' || b == 0x7f || // control character + b == '%' || b == '+' || // URI escape + b == ':' || // Windows drive letter + b == '@' || b == '&' || b == '?' { // authority or query + goto slow + } + } + return rest, nil + } +slow: + + u, err := url.ParseRequestURI(string(uri)) + if err != nil { + return "", err + } + if u.Scheme != fileScheme { + return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri) + } + // If the URI is a Windows URI, we trim the leading "/" and uppercase + // the drive letter, which will never be case sensitive. + if isWindowsDriveURIPath(u.Path) { + u.Path = strings.ToUpper(string(u.Path[1])) + u.Path[2:] + } + + return u.Path, nil +} + +func URIFromURI(s string) URI { + if !strings.HasPrefix(s, "file://") { + return URI(s) + } + + if !strings.HasPrefix(s, "file:///") { + // VS Code sends URLs with only two slashes, which are invalid. golang/go#39789. + s = "file:///" + s[len("file://"):] + } + // Even though the input is a URI, it may not be in canonical form. VS Code + // in particular over-escapes :, @, etc. Unescape and re-encode to canonicalize. + path, err := url.PathUnescape(s[len("file://"):]) + if err != nil { + panic(err) + } + + // File URIs from Windows may have lowercase drive letters. + // Since drive letters are guaranteed to be case insensitive, + // we change them to uppercase to remain consistent. + // For example, file:///c:/x/y/z becomes file:///C:/x/y/z. + if isWindowsDriveURIPath(path) { + path = path[:1] + strings.ToUpper(string(path[1])) + path[2:] + } + u := url.URL{Scheme: fileScheme, Path: path} + return URI(u.String()) +} + +// CompareURI performs a three-valued comparison of two URIs. +// Lexically unequal URIs may compare equal if they are "file:" URIs +// that share the same base name (ignoring case) and denote the same +// file device/inode, according to stat(2). +func CompareURI(a, b URI) int { + if equalURI(a, b) { + return 0 + } + if a < b { + return -1 + } + return 1 +} + +func equalURI(a, b URI) bool { + if a == b { + return true + } + // If we have the same URI basename, we may still have the same file URIs. + if !strings.EqualFold(path.Base(string(a)), path.Base(string(b))) { + return false + } + fa, err := filename(a) + if err != nil { + return false + } + fb, err := filename(b) + if err != nil { + return false + } + // Stat the files to check if they are equal. + infoa, err := os.Stat(filepath.FromSlash(fa)) + if err != nil { + return false + } + infob, err := os.Stat(filepath.FromSlash(fb)) + if err != nil { + return false + } + return os.SameFile(infoa, infob) +} + +// URIFromPath returns a span URI for the supplied file path. +// +// For empty paths, URIFromPath returns the empty URI "". +// For non-empty paths, URIFromPath returns a uri with the file:// scheme. +func URIFromPath(path string) URI { + if path == "" { + return "" + } + // Handle standard library paths that contain the literal "$GOROOT". + // TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT. + const prefix = "$GOROOT" + if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) { + suffix := path[len(prefix):] + path = runtime.GOROOT() + suffix + } + if !isWindowsDrivePath(path) { + if abs, err := filepath.Abs(path); err == nil { + path = abs + } + } + // Check the file path again, in case it became absolute. + if isWindowsDrivePath(path) { + path = "/" + strings.ToUpper(string(path[0])) + path[1:] + } + path = filepath.ToSlash(path) + u := url.URL{ + Scheme: fileScheme, + Path: path, + } + return URI(u.String()) +} + +// isWindowsDrivePath returns true if the file path is of the form used by +// Windows. We check if the path begins with a drive letter, followed by a ":". +// For example: C:/x/y/z. +func isWindowsDrivePath(path string) bool { + if len(path) < 3 { + return false + } + return unicode.IsLetter(rune(path[0])) && path[1] == ':' +} + +// isWindowsDriveURIPath returns true if the file URI is of the format used by +// Windows URIs. The url.Parse package does not specially handle Windows paths +// (see golang/go#6027), so we check if the URI path has a drive prefix (e.g. "/C:"). +func isWindowsDriveURIPath(uri string) bool { + if len(uri) < 4 { + return false + } + return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' +} diff --git a/gopls/golang_org_x_tools_gopls/span/utf16.go b/gopls/golang_org_x_tools_gopls/span/utf16.go new file mode 100644 index 0000000..0f8e1bc --- /dev/null +++ b/gopls/golang_org_x_tools_gopls/span/utf16.go @@ -0,0 +1,105 @@ +// 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 span + +import ( + "fmt" + "unicode/utf8" +) + +// ToUTF16Column calculates the utf16 column expressed by the point given the +// supplied file contents. +// This is used to convert from the native (always in bytes) column +// representation and the utf16 counts used by some editors. +// +// TODO(adonovan): this function is unused except by its test. Delete, +// or consolidate with (*protocol.ColumnMapper).utf16Column. +func ToUTF16Column(p Point, content []byte) (int, error) { + if !p.HasPosition() { + return -1, fmt.Errorf("ToUTF16Column: point is missing position") + } + if !p.HasOffset() { + return -1, fmt.Errorf("ToUTF16Column: point is missing offset") + } + offset := p.Offset() // 0-based + colZero := p.Column() - 1 // 0-based + if colZero == 0 { + // 0-based column 0, so it must be chr 1 + return 1, nil + } else if colZero < 0 { + return -1, fmt.Errorf("ToUTF16Column: column is invalid (%v)", colZero) + } + // work out the offset at the start of the line using the column + lineOffset := offset - colZero + if lineOffset < 0 || offset > len(content) { + return -1, fmt.Errorf("ToUTF16Column: offsets %v-%v outside file contents (%v)", lineOffset, offset, len(content)) + } + // Use the offset to pick out the line start. + // This cannot panic: offset > len(content) and lineOffset < offset. + start := content[lineOffset:] + + // Now, truncate down to the supplied column. + start = start[:colZero] + + cnt := 0 + for _, r := range string(start) { + cnt++ + if r > 0xffff { + cnt++ + } + } + return cnt + 1, nil // the +1 is for 1-based columns +} + +// FromUTF16Column advances the point by the utf16 character offset given the +// supplied line contents. +// This is used to convert from the utf16 counts used by some editors to the +// native (always in bytes) column representation. +// +// The resulting Point always has an offset. +// +// TODO: it looks like this may incorrectly confer a "position" to the +// resulting Point, when it shouldn't. If p.HasPosition() == false, the +// resulting Point will return p.HasPosition() == true, but have the wrong +// position. +func FromUTF16Column(p Point, chr int, content []byte) (Point, error) { + if !p.HasOffset() { + return Point{}, fmt.Errorf("FromUTF16Column: point is missing offset") + } + // if chr is 1 then no adjustment needed + if chr <= 1 { + return p, nil + } + if p.Offset() >= len(content) { + return p, fmt.Errorf("FromUTF16Column: offset (%v) greater than length of content (%v)", p.Offset(), len(content)) + } + remains := content[p.Offset():] + // scan forward the specified number of characters + for count := 1; count < chr; count++ { + if len(remains) <= 0 { + return Point{}, fmt.Errorf("FromUTF16Column: chr goes beyond the content") + } + r, w := utf8.DecodeRune(remains) + if r == '\n' { + // Per the LSP spec: + // + // > If the character value is greater than the line length it + // > defaults back to the line length. + break + } + remains = remains[w:] + if r >= 0x10000 { + // a two point rune + count++ + // if we finished in a two point rune, do not advance past the first + if count >= chr { + break + } + } + p.v.Column += w + p.v.Offset += w + } + return p, nil +} diff --git a/gopls/gopls.go b/gopls/gopls.go new file mode 100644 index 0000000..81e8a30 --- /dev/null +++ b/gopls/gopls.go @@ -0,0 +1,97 @@ +package gopls + +import ( + "errors" + "log" + "net" + "net/rpc" + "net/rpc/jsonrpc" + + _ "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" + "github.com/visualfc/gotools/pkg/command" +) + +var Command = &command.Command{ + Run: runGopls, + UsageLine: "gopls", + Short: "golang gopls util", + Long: `golang golsp client for Go language server`, +} + +var ( + flagServer bool + flagCloseServer bool + flagRpcAddress string + flagGoplsPath string +) + +func init() { + Command.Flag.BoolVar(&flagServer, "s", false, "run gopls server") + Command.Flag.BoolVar(&flagCloseServer, "close", false, "close gopls server") + Command.Flag.StringVar(&flagRpcAddress, "addr", "127.0.0.1:37373", "rpc server listen address") + Command.Flag.StringVar(&flagGoplsPath, "gopls", "gopls", "gopls filepath") +} + +func runGopls(cmd *command.Command, args []string) error { + if flagServer { + return runServer() + } else { + return runClient() + } + return nil +} + +func runClient() error { + conn, err := net.Dial("tcp", flagRpcAddress) + if err != nil { + return err + } + defer conn.Close() + client := &RPCClient{client: rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))} + if flagCloseServer { + var out ExitOut + client.Exit(ExitIn{0}, &out) + } + return nil +} + +func runServer() error { + srv := NewServer(&Client{}) + err := srv.run(flagGoplsPath, "-v", "-rpc.trace") + if err != nil { + return err + } + rcvr := &RPCServer{server: srv.server, exited: make(chan int)} + rpc.RegisterName(RPCServerName, rcvr) + + listener, err := net.Listen("tcp", flagRpcAddress) + if err != nil { + return err + } + defer listener.Close() + + conn := make(chan net.Conn) + go func() { + for { + c, err := listener.Accept() + if err != nil { + if errors.Is(err, net.ErrClosed) { + log.Println("exit") + break + } + panic(err) + } + conn <- c + } + }() + for { + select { + case c := <-conn: + go rpc.ServeCodec(jsonrpc.NewServerCodec(c)) + case code := <-rcvr.exited: + log.Println("exit", code) + return nil + } + } + return nil +} diff --git a/gopls/rpc.go b/gopls/rpc.go new file mode 100644 index 0000000..7ae1238 --- /dev/null +++ b/gopls/rpc.go @@ -0,0 +1,126 @@ +package gopls + +import ( + "context" + "fmt" + "net/rpc" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +const ( + RPCServerName = "RPCServer" +) + +type OpenFileIn struct { + URL string + Text string +} + +type OpenFileOut struct{} + +type CloseFileIn struct { + URL string +} + +type CloseFileOut struct{} + +type CompletionIn struct { + URI string + Line uint32 + Character uint32 +} + +type CompletionItem struct { + Label string + Details string + Kind string + Doc string +} + +type CompletionOut struct { + items []*CompletionItem +} + +type ExitIn struct { + Code int +} +type ExitOut struct{} + +type RPCServer struct { + server protocol.Server + exited chan int +} + +func (s *RPCServer) OpenFile(in OpenFileIn, out *OpenFileOut) error { + return s.server.DidOpen(context.Background(), &protocol.DidOpenTextDocumentParams{ + TextDocument: protocol.TextDocumentItem{ + URI: protocol.URIFromPath(in.URL), + LanguageID: "go", + Version: 1, + Text: in.Text, + }, + }) +} + +func (s *RPCServer) CloseFile(in CloseFileIn, out *CloseFileOut) error { + return s.server.DidClose(context.Background(), &protocol.DidCloseTextDocumentParams{ + TextDocument: protocol.TextDocumentIdentifier{ + URI: protocol.URIFromPath(in.URL), + }, + }) +} + +func (s *RPCServer) Completion(in CompletionIn, out *CompletionOut) error { + list, err := s.server.Completion(context.Background(), &protocol.CompletionParams{ + TextDocumentPositionParams: protocol.TextDocumentPositionParams{ + TextDocument: protocol.TextDocumentIdentifier{ + URI: protocol.URIFromPath(in.URI), + }, + Position: protocol.Position{ + Line: in.Line, + Character: in.Character, + }, + }, + }) + for _, item := range list.Items { + out.items = append(out.items, &CompletionItem{ + Label: item.Label, + Details: item.Detail, + Kind: fmt.Sprintf("%v", item.Kind), + Doc: item.Documentation, + }) + } + return err +} + +func (s *RPCServer) Exit(in ExitIn, out *ExitOut) error { + go func() { + s.exited <- in.Code + }() + return nil +} + +type RPCClient struct { + client *rpc.Client +} + +func (c *RPCClient) call(method string, args interface{}, reply interface{}) error { + return c.client.Call(RPCServerName+"."+method, args, reply) +} + +func (c *RPCClient) OpenFile(in OpenFileIn, out *OpenFileOut) error { + return c.call("OpenFile", in, out) +} + +func (c *RPCClient) CloseFile(in CloseFileIn, out *CloseFileOut) error { + return c.call("CloseFile", in, out) +} + +func (c *RPCClient) Completion(in CompletionIn, out *CompletionOut) error { + return c.call("Completion", in, out) +} + +func (c *RPCClient) Exit(in ExitIn, out *ExitOut) error { + return c.call("Exit", in, out) +} diff --git a/gopls/server.go b/gopls/server.go new file mode 100644 index 0000000..f47a6fd --- /dev/null +++ b/gopls/server.go @@ -0,0 +1,88 @@ +package gopls + +import ( + "bufio" + "context" + "fmt" + "log" + "os" + "os/exec" + + "github.com/visualfc/gotools/gopls/golang_org_x_tools/fakenet" + "github.com/visualfc/gotools/gopls/golang_org_x_tools/jsonrpc2" + "github.com/visualfc/gotools/gopls/golang_org_x_tools_gopls/lsp/protocol" +) + +type Server struct { + cmd *exec.Cmd + server protocol.Server + client protocol.Client + cancel context.CancelFunc +} + +func NewServer(client protocol.Client) *Server { + return &Server{client: client} +} + +func (s *Server) run(goplscmd string, args ...string) error { + cmd := exec.Command(goplscmd, args...) + cmd.Env = os.Environ() + stderr, err := cmd.StderrPipe() + if err != nil { + return fmt.Errorf("failed to create stderr pipe for gopls: %v", err) + } + go func() error { + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + log.Printf("gopls stderr: %v\n", scanner.Text()) + } + if err := scanner.Err(); err != nil { + return fmt.Errorf("reading standard input: %v", err) + } + return nil + }() + stdout, err := cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("failed to create stdout pipe for gopls: %v", err) + } + stdin, err := cmd.StdinPipe() + if err != nil { + return fmt.Errorf("failed to create stdin pipe for gopls: %v", err) + } + if err := cmd.Start(); err != nil { + return fmt.Errorf("failed to start gopls: %v", err) + } + go func() (err error) { + if err = cmd.Wait(); err != nil { + err = fmt.Errorf("got error running gopls: %v", err) + } + return nil + }() + + fakeconn := fakenet.NewConn("stdio", stdout, stdin) + stream := jsonrpc2.NewHeaderStream(fakeconn) + ctxt, cancel := context.WithCancel(context.Background()) + conn := jsonrpc2.NewConn(stream) + server := protocol.ServerDispatcher(conn) + + handler := protocol.ClientHandler(s.client, jsonrpc2.MethodNotFound) + handler = protocol.Handlers(handler) + ctxt = protocol.WithClient(ctxt, s.client) + + go func() { + conn.Go(ctxt, handler) + <-conn.Done() + }() + _, err = server.Initialize(context.Background(), &protocol.ParamInitialize{}) + if err != nil { + return err + } + err = server.Initialized(context.Background(), &protocol.InitializedParams{}) + if err != nil { + return err + } + s.server = server + s.cancel = cancel + s.cmd = cmd + return nil +} diff --git a/main.go b/main.go index b45be85..a5a5014 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "github.com/visualfc/gotools/goapi" "github.com/visualfc/gotools/godoc" "github.com/visualfc/gotools/gofmt" + "github.com/visualfc/gotools/gopls" "github.com/visualfc/gotools/gopresent" "github.com/visualfc/gotools/gotest" "github.com/visualfc/gotools/jsonfmt" @@ -43,6 +44,7 @@ func init() { command.Register(debugflags.Command) command.Register(pkgcheck.Command) command.Register(godoc.Command) + command.Register(gopls.Command) } func main() {