rename gomarkdown/markdown to status-im/markdown

This resolves a dependency conflict we have with MatterBridge
which was using a newer version of the same package.
This resulted in a JSON marshalling bug that would crash the bridge.

Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
Jakub Sokołowski 2020-02-10 17:52:43 +01:00 committed by Jakub
parent c012576541
commit cca47ef6e1
57 changed files with 481 additions and 1634 deletions

View File

@ -186,7 +186,15 @@ clean-release:
rm -rf $(RELEASE_DIR) rm -rf $(RELEASE_DIR)
lint-fix: lint-fix:
find . -name '*.go' -and -not -name '*.pb.go' -and -not -name 'bindata*' -and -not -name 'migrations.go' -and -not -wholename '*/vendor/*' -exec goimports -local 'github.com/ethereum/go-ethereum,github.com/status-im/status-go' -w {} \; find . \
-name '*.go' \
-and -not -name '*.pb.go' \
-and -not -name 'bindata*' \
-and -not -name 'migrations.go' \
-and -not -wholename '*/vendor/*' \
-exec goimports \
-local 'github.com/ethereum/go-ethereum,github.com/status-im/status-go,github.com/status-im/markdown' \
-w {} \;
$(MAKE) vendor $(MAKE) vendor
check-existing-release: check-existing-release:

View File

@ -366,8 +366,8 @@ func (b *nimbusStatusBackend) loadNodeConfig() (*params.NodeConfig, error) {
// which is set at the compile time. // which is set at the compile time.
// What's cached is usually outdated so we overwrite it here. // What's cached is usually outdated so we overwrite it here.
conf.Version = params.Version conf.Version = params.Version
// Replace all relative paths with absolute // Replace all relative paths with absolute
conf.DataDir = filepath.Join(b.rootDataDir, conf.DataDir) conf.DataDir = filepath.Join(b.rootDataDir, conf.DataDir)
conf.ShhextConfig.BackupDisabledDataDir = filepath.Join(b.rootDataDir, conf.ShhextConfig.BackupDisabledDataDir) conf.ShhextConfig.BackupDisabledDataDir = filepath.Join(b.rootDataDir, conf.ShhextConfig.BackupDisabledDataDir)
if len(conf.LogDir) == 0 { if len(conf.LogDir) == 0 {

6
go.mod
View File

@ -8,8 +8,6 @@ replace github.com/Sirupsen/logrus v1.4.2 => github.com/sirupsen/logrus v1.4.2
replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1 replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1
replace github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba
require ( require (
github.com/beevik/ntp v0.2.0 github.com/beevik/ntp v0.2.0
github.com/btcsuite/btcd v0.20.1-beta github.com/btcsuite/btcd v0.20.1-beta
@ -20,7 +18,6 @@ require (
github.com/golang-migrate/migrate/v4 v4.8.0 // indirect github.com/golang-migrate/migrate/v4 v4.8.0 // indirect
github.com/golang/mock v1.3.1 github.com/golang/mock v1.3.1
github.com/golang/protobuf v1.3.2 github.com/golang/protobuf v1.3.2
github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 // indirect
@ -41,6 +38,7 @@ require (
github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a github.com/russolsen/transit v0.0.0-20180705123435-0794b4c4505a
github.com/status-im/doubleratchet v3.0.0+incompatible github.com/status-im/doubleratchet v3.0.0+incompatible
github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e // indirect github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e // indirect
github.com/status-im/markdown v0.0.0-20200210164614-b9fe92168122
github.com/status-im/migrate/v4 v4.6.2-status.2 github.com/status-im/migrate/v4 v4.6.2-status.2
github.com/status-im/rendezvous v1.3.0 github.com/status-im/rendezvous v1.3.0
github.com/status-im/status-go/extkeys v1.1.0 github.com/status-im/status-go/extkeys v1.1.0
@ -53,7 +51,7 @@ require (
github.com/wealdtech/go-ens/v3 v3.3.0 github.com/wealdtech/go-ens/v3 v3.3.0
go.uber.org/zap v1.13.0 go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c
golang.org/x/tools v0.0.0-20200116062425-473961ec044c // indirect golang.org/x/tools v0.0.0-20200211045251-2de505fc5306 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0 gopkg.in/go-playground/validator.v9 v9.31.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0

10
go.sum
View File

@ -633,8 +633,8 @@ github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 h1:ju5UTwk5Od
github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e h1:iT/UJdf+SzbgkIPDe/RmlCfLEQ+ab4UMl6toBl9CGZA= github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e h1:iT/UJdf+SzbgkIPDe/RmlCfLEQ+ab4UMl6toBl9CGZA=
github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba h1:Ut2CKuG+L9eWFL7dTEPuLE+RKecUYBkDNhqXSIgY92U= github.com/status-im/markdown v0.0.0-20200210164614-b9fe92168122 h1:q18rWqbHHXZGKPAIuvlb2wAAA3XzlMbkEey2bhKf8GY=
github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba/go.mod h1:tmG2bxyvZ2EItDO5JewbdFvV45j13IYQgvnMJ3+qAaE= github.com/status-im/markdown v0.0.0-20200210164614-b9fe92168122/go.mod h1:9yR8woqkJIHs3sf9pEjYvaGfmhsXR1leEMAX6+Z5y+M=
github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8= github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8=
github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8= github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8=
github.com/status-im/rendezvous v1.3.0 h1:7RK/MXXW+tlm0asKm1u7Qp7Yni6AO29a7j8+E4Lbjg4= github.com/status-im/rendezvous v1.3.0 h1:7RK/MXXW+tlm0asKm1u7Qp7Yni6AO29a7j8+E4Lbjg4=
@ -748,6 +748,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -829,9 +830,10 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200116062425-473961ec044c h1:D0OxfnjPaEGt7AluXNompYUYGhoY3u6+bValgqfd1vE= golang.org/x/tools v0.0.0-20200211045251-2de505fc5306 h1:5gd/+xxg4X7hx+44aG6Sdh17vBwwRFacMaSfqF4wkWk=
golang.org/x/tools v0.0.0-20200116062425-473961ec044c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200211045251-2de505fc5306/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=

View File

@ -12,12 +12,13 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/whisper/v6" "github.com/status-im/status-go/whisper/v6"
"github.com/stretchr/testify/require"
) )
func TestPostgresDB_BuildIteratorWithBloomFilter(t *testing.T) { func TestPostgresDB_BuildIteratorWithBloomFilter(t *testing.T) {

View File

@ -7,7 +7,7 @@ import (
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"github.com/gomarkdown/markdown" "github.com/status-im/markdown"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
) )

View File

@ -1,9 +1,10 @@
package ext package ext
import ( import (
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
enstypes "github.com/status-im/status-go/eth-node/types/ens" enstypes "github.com/status-im/status-go/eth-node/types/ens"
"go.uber.org/zap"
) )
type TestNodeWrapper struct { type TestNodeWrapper struct {

View File

@ -1,21 +1,21 @@
# Markdown Parser and HTML Renderer for Go # Markdown Parser and HTML Renderer for Go
[![GoDoc](https://godoc.org/github.com/gomarkdown/markdown?status.svg)](https://godoc.org/github.com/gomarkdown/markdown) [![codecov](https://codecov.io/gh/gomarkdown/markdown/branch/master/graph/badge.svg)](https://codecov.io/gh/gomarkdown/markdown) [![GoDoc](https://godoc.org/github.com/status-im/markdown?status.svg)](https://godoc.org/github.com/gomarkdown/markdown) [![codecov](https://codecov.io/gh/gomarkdown/markdown/branch/master/graph/badge.svg)](https://codecov.io/gh/gomarkdown/markdown)
Package `github.com/gomarkdown/markdown` is a very fast Go library for parsing [Markdown](https://daringfireball.net/projects/markdown/) documents and rendering them to HTML. Package `github.com/status-im/markdown` is a very fast Go library for parsing [Markdown](https://daringfireball.net/projects/markdown/) documents and rendering them to HTML.
It's fast and supports common extensions. It's fast and supports common extensions.
## Installation ## Installation
go get -u github.com/gomarkdown/markdown go get -u github.com/status-im/markdown
API Docs: API Docs:
- https://godoc.org/github.com/gomarkdown/markdown : top level package - https://godoc.org/github.com/status-im/markdown : top level package
- https://godoc.org/github.com/gomarkdown/markdown/ast : defines abstract syntax tree of parsed markdown document - https://godoc.org/github.com/status-im/markdown/ast : defines abstract syntax tree of parsed markdown document
- https://godoc.org/github.com/gomarkdown/markdown/parser : parser - https://godoc.org/github.com/status-im/markdown/parser : parser
- https://godoc.org/github.com/gomarkdown/markdown/html : html renderer - https://godoc.org/github.com/status-im/markdown/html : html renderer
## Usage ## Usage
@ -30,14 +30,14 @@ output := markdown.ToHTML(md, nil, nil)
Markdown format is loosely specified and there are multiple extensions invented after original specification was created. Markdown format is loosely specified and there are multiple extensions invented after original specification was created.
The parser supports several [extensions](https://godoc.org/github.com/gomarkdown/markdown/parser#Extensions). The parser supports several [extensions](https://godoc.org/github.com/status-im/markdown/parser#Extensions).
Default parser uses most common `parser.CommonExtensions` but you can easily use parser with custom extension: Default parser uses most common `parser.CommonExtensions` but you can easily use parser with custom extension:
```go ```go
import ( import (
"github.com/gomarkdown/markdown" "github.com/status-im/markdown"
"github.com/gomarkdown/markdown/parser" "github.com/status-im/markdown/parser"
) )
extensions := parser.CommonExtensions | parser.AutoHeadingIDs extensions := parser.CommonExtensions | parser.AutoHeadingIDs
@ -49,14 +49,14 @@ html := markdown.ToHTML(md, parser, nil)
## Customizing HTML renderer ## Customizing HTML renderer
Similarly, HTML renderer can be configured with different [options](https://godoc.org/github.com/gomarkdown/markdown/html#RendererOptions) Similarly, HTML renderer can be configured with different [options](https://godoc.org/github.com/status-im/markdown/html#RendererOptions)
Here's how to use a custom renderer: Here's how to use a custom renderer:
```go ```go
import ( import (
"github.com/gomarkdown/markdown" "github.com/status-im/markdown"
"github.com/gomarkdown/markdown/html" "github.com/status-im/markdown/html"
) )
htmlFlags := html.CommonFlags | html.HrefTargetBlank htmlFlags := html.CommonFlags | html.HrefTargetBlank
@ -69,7 +69,7 @@ html := markdown.ToHTML(md, nil, renderer)
HTML renderer also supports reusing most of the logic and overriding rendering of only specifc nodes. HTML renderer also supports reusing most of the logic and overriding rendering of only specifc nodes.
You can provide [RenderNodeFunc](https://godoc.org/github.com/gomarkdown/markdown/html#RenderNodeFunc) in [RendererOptions](https://godoc.org/github.com/gomarkdown/markdown/html#RendererOptions). You can provide [RenderNodeFunc](https://godoc.org/github.com/status-im/markdown/html#RenderNodeFunc) in [RendererOptions](https://godoc.org/github.com/gomarkdown/markdown/html#RendererOptions).
The function is called for each node in AST, you can implement custom rendering logic and tell HTML renderer to skip rendering this node. The function is called for each node in AST, you can implement custom rendering logic and tell HTML renderer to skip rendering this node.
@ -77,9 +77,9 @@ Here's the simplest example that drops all code blocks from the output:
````go ````go
import ( import (
"github.com/gomarkdown/markdown" "github.com/status-im/markdown"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
"github.com/gomarkdown/markdown/html" "github.com/status-im/markdown/html"
) )
// return (ast.GoToNext, true) to tell html renderer to skip rendering this node // return (ast.GoToNext, true) to tell html renderer to skip rendering this node
@ -113,7 +113,7 @@ Here's an example of simple usage with Bluemonday:
```go ```go
import ( import (
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
"github.com/gomarkdown/markdown" "github.com/status-im/markdown"
) )
// ... // ...
@ -152,7 +152,7 @@ To run: `mdtohtml input-file [output-file]`
NOTE: "safety" in this context means _runtime safety only_. In order to NOTE: "safety" in this context means _runtime safety only_. In order to
protect yourself against JavaScript injection in untrusted content, see protect yourself against JavaScript injection in untrusted content, see
[this example](https://github.com/gomarkdown/markdown#sanitize-untrusted-content). [this example](https://github.com/status-im/markdown#sanitize-untrusted-content).
- **Fast**. It is fast enough to render on-demand in - **Fast**. It is fast enough to render on-demand in
most web applications without having to cache the output. most web applications without having to cache the output.

View File

@ -16,9 +16,9 @@ Customizing parsing and HTML rendering
You can customize parser and HTML renderer: You can customize parser and HTML renderer:
import ( import (
"github.com/gomarkdown/markdown/parser" "github.com/status-im/markdown/parser"
"github.com/gomarkdown/markdown/renderer" "github.com/status-im/markdown/renderer"
"github.com/gomarkdown/markdown" "github.com/status-im/markdown"
) )
extensions := parser.CommonExtensions | parser.AutoHeadingIDs extensions := parser.CommonExtensions | parser.AutoHeadingIDs
p := parser.NewWithExtensions(extensions) p := parser.NewWithExtensions(extensions)

View File

@ -1,4 +1,4 @@
module github.com/gomarkdown/markdown module github.com/status-im/markdown
go 1.12 go 1.12

View File

@ -4,8 +4,8 @@ import (
"bytes" "bytes"
"io" "io"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
"github.com/gomarkdown/markdown/parser" "github.com/status-im/markdown/parser"
) )
// Renderer is an interface for implementing custom renderers. // Renderer is an interface for implementing custom renderers.

View File

@ -3,7 +3,7 @@ package parser
import ( import (
"bytes" "bytes"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// returns aisde prefix length // returns aisde prefix length

View File

@ -3,7 +3,7 @@ package parser
import ( import (
"bytes" "bytes"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// attribute parses a (potential) block attribute and adds it to p. // attribute parses a (potential) block attribute and adds it to p.

View File

@ -6,7 +6,7 @@ import (
"regexp" "regexp"
"unicode" "unicode"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// Parsing block-level elements. // Parsing block-level elements.

View File

@ -3,7 +3,7 @@ package parser
import ( import (
"bytes" "bytes"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// citation parses a citation. In its most simple form [@ref], we allow multiple // citation parses a citation. In its most simple form [@ref], we allow multiple

View File

@ -3,7 +3,7 @@ package parser
import ( import (
"bytes" "bytes"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// sFigureLine checks if there's a figure line (e.g., !--- ) at the beginning of data, // sFigureLine checks if there's a figure line (e.g., !--- ) at the beginning of data,

View File

@ -5,7 +5,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// Parsing of inline elements // Parsing of inline elements

View File

@ -3,7 +3,7 @@ package parser
import ( import (
"bytes" "bytes"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
func (p *Parser) documentMatter(data []byte) int { func (p *Parser) documentMatter(data []byte) int {

View File

@ -1,7 +1,7 @@
package parser package parser
import ( import (
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// Flags control optional behavior of parser. // Flags control optional behavior of parser.

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// Extensions is a bitmask of enabled parser extensions. // Extensions is a bitmask of enabled parser extensions.

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/gomarkdown/markdown/ast" "github.com/status-im/markdown/ast"
) )
// parse '(#r)', where r does not contain spaces. Or. // parse '(#r)', where r does not contain spaces. Or.

View File

@ -1,6 +1,6 @@
/* /*
The analysis package defines the interface between a modular static Package analysis defines the interface between a modular static
analysis and an analysis driver program. analysis and an analysis driver program.

View File

@ -275,9 +275,10 @@ func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (del
// We deleted an entry but now there may be // We deleted an entry but now there may be
// a blank line-sized hole where the import was. // a blank line-sized hole where the import was.
if line-lastLine > 1 { if line-lastLine > 1 || !gen.Rparen.IsValid() {
// There was a blank line immediately preceding the deleted import, // There was a blank line immediately preceding the deleted import,
// so there's no need to close the hole. // so there's no need to close the hole. The right parenthesis is
// invalid after AddImport to an import statement without parenthesis.
// Do nothing. // Do nothing.
} else if line != fset.File(gen.Rparen).LineCount() { } else if line != fset.File(gen.Rparen).LineCount() {
// There was no blank line. Close the hole. // There was no blank line. Close the hole.

View File

@ -90,7 +90,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
// The types argument, if non-empty, enables type-based filtering of // The types argument, if non-empty, enables type-based filtering of
// events. The function f if is called only for nodes whose type // events. The function f if is called only for nodes whose type
// matches an element of the types slice. // matches an element of the types slice.
func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (prune bool)) { func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) {
mask := maskOf(types) mask := maskOf(types)
for i := 0; i < len(in.events); { for i := 0; i < len(in.events); {
ev := in.events[i] ev := in.events[i]
@ -114,7 +114,7 @@ func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (prun
// supplies each call to f an additional argument, the current // supplies each call to f an additional argument, the current
// traversal stack. The stack's first element is the outermost node, // traversal stack. The stack's first element is the outermost node,
// an *ast.File; its last is the innermost, n. // an *ast.File; its last is the innermost, n.
func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (prune bool)) { func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) {
mask := maskOf(types) mask := maskOf(types)
var stack []ast.Node var stack []ast.Node
for i := 0; i < len(in.events); { for i := 0; i < len(in.events); {

View File

@ -344,7 +344,7 @@ func (p *parser) expectKeyword(keyword string) {
// PackageId = string_lit . // PackageId = string_lit .
// //
func (p *parser) parsePackageId() string { func (p *parser) parsePackageID() string {
id, err := strconv.Unquote(p.expect(scanner.String)) id, err := strconv.Unquote(p.expect(scanner.String))
if err != nil { if err != nil {
p.error(err) p.error(err)
@ -384,7 +384,7 @@ func (p *parser) parseDotIdent() string {
// //
func (p *parser) parseQualifiedName() (id, name string) { func (p *parser) parseQualifiedName() (id, name string) {
p.expect('@') p.expect('@')
id = p.parsePackageId() id = p.parsePackageID()
p.expect('.') p.expect('.')
// Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields. // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
if p.tok == '?' { if p.tok == '?' {
@ -696,7 +696,7 @@ func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
// Complete requires the type's embedded interfaces to be fully defined, // Complete requires the type's embedded interfaces to be fully defined,
// but we do not define any // but we do not define any
return types.NewInterface(methods, nil).Complete() return newInterface(methods, nil).Complete()
} }
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . // ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
@ -785,7 +785,7 @@ func (p *parser) parseType(parent *types.Package) types.Type {
func (p *parser) parseImportDecl() { func (p *parser) parseImportDecl() {
p.expectKeyword("import") p.expectKeyword("import")
name := p.parsePackageName() name := p.parsePackageName()
p.getPkg(p.parsePackageId(), name) p.getPkg(p.parsePackageID(), name)
} }
// int_lit = [ "+" | "-" ] { "0" ... "9" } . // int_lit = [ "+" | "-" ] { "0" ... "9" } .

View File

@ -6,17 +6,17 @@ package packages
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"go/types" "go/types"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp" "sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -24,8 +24,6 @@ import (
"unicode" "unicode"
"golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/go/internal/packagesdriver"
"golang.org/x/tools/internal/gopathwalk"
"golang.org/x/tools/internal/semver"
) )
// debug controls verbose logging. // debug controls verbose logging.
@ -44,16 +42,21 @@ type responseDeduper struct {
dr *driverResponse dr *driverResponse
} }
// init fills in r with a driverResponse. func newDeduper() *responseDeduper {
func (r *responseDeduper) init(dr *driverResponse) { return &responseDeduper{
r.dr = dr dr: &driverResponse{},
r.seenRoots = map[string]bool{} seenRoots: map[string]bool{},
r.seenPackages = map[string]*Package{} seenPackages: map[string]*Package{},
}
}
// addAll fills in r with a driverResponse.
func (r *responseDeduper) addAll(dr *driverResponse) {
for _, pkg := range dr.Packages { for _, pkg := range dr.Packages {
r.seenPackages[pkg.ID] = pkg r.addPackage(pkg)
} }
for _, root := range dr.Roots { for _, root := range dr.Roots {
r.seenRoots[root] = true r.addRoot(root)
} }
} }
@ -73,25 +76,47 @@ func (r *responseDeduper) addRoot(id string) {
r.dr.Roots = append(r.dr.Roots, id) r.dr.Roots = append(r.dr.Roots, id)
} }
// goInfo contains global information from the go tool. type golistState struct {
type goInfo struct { cfg *Config
rootDirs map[string]string ctx context.Context
env goEnv
envOnce sync.Once
goEnvError error
goEnv map[string]string
rootsOnce sync.Once
rootDirsError error
rootDirs map[string]string
// vendorDirs caches the (non)existence of vendor directories.
vendorDirs map[string]bool
} }
type goEnv struct { // getEnv returns Go environment variables. Only specific variables are
modulesOn bool // populated -- computing all of them is slow.
func (state *golistState) getEnv() (map[string]string, error) {
state.envOnce.Do(func() {
var b *bytes.Buffer
b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH")
if state.goEnvError != nil {
return
}
state.goEnv = make(map[string]string)
decoder := json.NewDecoder(b)
if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil {
return
}
})
return state.goEnv, state.goEnvError
} }
func determineEnv(cfg *Config) goEnv { // mustGetEnv is a convenience function that can be used if getEnv has already succeeded.
buf, err := invokeGo(cfg, "env", "GOMOD") func (state *golistState) mustGetEnv() map[string]string {
env, err := state.getEnv()
if err != nil { if err != nil {
return goEnv{} panic(fmt.Sprintf("mustGetEnv: %v", err))
} }
gomod := bytes.TrimSpace(buf.Bytes())
env := goEnv{}
env.modulesOn = len(gomod) > 0
return env return env
} }
@ -99,47 +124,38 @@ func determineEnv(cfg *Config) goEnv {
// the build system package structure. // the build system package structure.
// See driver for more details. // See driver for more details.
func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
var sizes types.Sizes // Make sure that any asynchronous go commands are killed when we return.
parentCtx := cfg.Context
if parentCtx == nil {
parentCtx = context.Background()
}
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()
response := newDeduper()
// Fill in response.Sizes asynchronously if necessary.
var sizeserr error var sizeserr error
var sizeswg sync.WaitGroup var sizeswg sync.WaitGroup
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
sizeswg.Add(1) sizeswg.Add(1)
go func() { go func() {
sizes, sizeserr = getSizes(cfg) var sizes types.Sizes
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
// types.SizesFor always returns nil or a *types.StdSizes.
response.dr.Sizes, _ = sizes.(*types.StdSizes)
sizeswg.Done() sizeswg.Done()
}() }()
} }
defer sizeswg.Wait()
// start fetching rootDirs state := &golistState{
var info goInfo cfg: cfg,
var rootDirsReady, envReady = make(chan struct{}), make(chan struct{}) ctx: ctx,
go func() { vendorDirs: map[string]bool{},
info.rootDirs = determineRootDirs(cfg)
close(rootDirsReady)
}()
go func() {
info.env = determineEnv(cfg)
close(envReady)
}()
getGoInfo := func() *goInfo {
<-rootDirsReady
<-envReady
return &info
}
// Ensure that we don't leak goroutines: Load is synchronous, so callers will
// not expect it to access the fields of cfg after the call returns.
defer getGoInfo()
// always pass getGoInfo to golistDriver
golistDriver := func(cfg *Config, patterns ...string) (*driverResponse, error) {
return golistDriver(cfg, getGoInfo, patterns...)
} }
// Determine files requested in contains patterns // Determine files requested in contains patterns
var containFiles []string var containFiles []string
var packagesNamed []string
restPatterns := make([]string, 0, len(patterns)) restPatterns := make([]string, 0, len(patterns))
// Extract file= and other [querytype]= patterns. Report an error if querytype // Extract file= and other [querytype]= patterns. Report an error if querytype
// doesn't exist. // doesn't exist.
@ -155,8 +171,6 @@ extractQueries:
containFiles = append(containFiles, value) containFiles = append(containFiles, value)
case "pattern": case "pattern":
restPatterns = append(restPatterns, value) restPatterns = append(restPatterns, value)
case "iamashamedtousethedisabledqueryname":
packagesNamed = append(packagesNamed, value)
case "": // not a reserved query case "": // not a reserved query
restPatterns = append(restPatterns, pattern) restPatterns = append(restPatterns, pattern)
default: default:
@ -172,52 +186,34 @@ extractQueries:
} }
} }
response := &responseDeduper{}
var err error
// See if we have any patterns to pass through to go list. Zero initial // See if we have any patterns to pass through to go list. Zero initial
// patterns also requires a go list call, since it's the equivalent of // patterns also requires a go list call, since it's the equivalent of
// ".". // ".".
if len(restPatterns) > 0 || len(patterns) == 0 { if len(restPatterns) > 0 || len(patterns) == 0 {
dr, err := golistDriver(cfg, restPatterns...) dr, err := state.createDriverResponse(restPatterns...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.init(dr) response.addAll(dr)
} else {
response.init(&driverResponse{})
} }
sizeswg.Wait()
if sizeserr != nil {
return nil, sizeserr
}
// types.SizesFor always returns nil or a *types.StdSizes
response.dr.Sizes, _ = sizes.(*types.StdSizes)
var containsCandidates []string
if len(containFiles) != 0 { if len(containFiles) != 0 {
if err := runContainsQueries(cfg, golistDriver, response, containFiles, getGoInfo); err != nil { if err := state.runContainsQueries(response, containFiles); err != nil {
return nil, err return nil, err
} }
} }
if len(packagesNamed) != 0 { modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
if err := runNamedQueries(cfg, golistDriver, response, packagesNamed); err != nil {
return nil, err
}
}
modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var containsCandidates []string
if len(containFiles) > 0 { if len(containFiles) > 0 {
containsCandidates = append(containsCandidates, modifiedPkgs...) containsCandidates = append(containsCandidates, modifiedPkgs...)
containsCandidates = append(containsCandidates, needPkgs...) containsCandidates = append(containsCandidates, needPkgs...)
} }
if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs, getGoInfo); err != nil { if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
return nil, err return nil, err
} }
// Check candidate packages for containFiles. // Check candidate packages for containFiles.
@ -246,28 +242,32 @@ extractQueries:
} }
} }
sizeswg.Wait()
if sizeserr != nil {
return nil, sizeserr
}
return response.dr, nil return response.dr, nil
} }
func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string, getGoInfo func() *goInfo) error { func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
if len(pkgs) == 0 { if len(pkgs) == 0 {
return nil return nil
} }
dr, err := driver(cfg, pkgs...) dr, err := state.createDriverResponse(pkgs...)
if err != nil { if err != nil {
return err return err
} }
for _, pkg := range dr.Packages { for _, pkg := range dr.Packages {
response.addPackage(pkg) response.addPackage(pkg)
} }
_, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo) _, needPkgs, err := state.processGolistOverlay(response)
if err != nil { if err != nil {
return err return err
} }
return addNeededOverlayPackages(cfg, driver, response, needPkgs, getGoInfo) return state.addNeededOverlayPackages(response, needPkgs)
} }
func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string, goInfo func() *goInfo) error { func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
for _, query := range queries { for _, query := range queries {
// TODO(matloob): Do only one query per directory. // TODO(matloob): Do only one query per directory.
fdir := filepath.Dir(query) fdir := filepath.Dir(query)
@ -277,44 +277,17 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q
if err != nil { if err != nil {
return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err) return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
} }
dirResponse, err := driver(cfg, pattern) dirResponse, err := state.createDriverResponse(pattern)
if err != nil || (len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].Errors) == 1) {
// There was an error loading the package. Try to load the file as an ad-hoc package. // If there was an error loading the package, or the package is returned
// Usually the error will appear in a returned package, but may not if we're in modules mode // with errors, try to load the file as an ad-hoc package.
// and the ad-hoc is located outside a module. // Usually the error will appear in a returned package, but may not if we're
// in module mode and the ad-hoc is located outside a module.
if err != nil || len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].GoFiles) == 0 &&
len(dirResponse.Packages[0].Errors) == 1 {
var queryErr error var queryErr error
dirResponse, queryErr = driver(cfg, query) if dirResponse, queryErr = state.adhocPackage(pattern, query); queryErr != nil {
if queryErr != nil { return err // return the original error
// Return the original error if the attempt to fall back failed.
return err
}
// If we get nothing back from `go list`, try to make this file into its own ad-hoc package.
if len(dirResponse.Packages) == 0 && queryErr == nil {
dirResponse.Packages = append(dirResponse.Packages, &Package{
ID: "command-line-arguments",
PkgPath: query,
GoFiles: []string{query},
CompiledGoFiles: []string{query},
Imports: make(map[string]*Package),
})
dirResponse.Roots = append(dirResponse.Roots, "command-line-arguments")
}
// Special case to handle issue #33482:
// If this is a file= query for ad-hoc packages where the file only exists on an overlay,
// and exists outside of a module, add the file in for the package.
if len(dirResponse.Packages) == 1 && (dirResponse.Packages[0].ID == "command-line-arguments" ||
filepath.ToSlash(dirResponse.Packages[0].PkgPath) == filepath.ToSlash(query)) {
if len(dirResponse.Packages[0].GoFiles) == 0 {
filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
// TODO(matloob): check if the file is outside of a root dir?
for path := range cfg.Overlay {
if path == filename {
dirResponse.Packages[0].Errors = nil
dirResponse.Packages[0].GoFiles = []string{path}
dirResponse.Packages[0].CompiledGoFiles = []string{path}
}
}
}
} }
} }
isRoot := make(map[string]bool, len(dirResponse.Roots)) isRoot := make(map[string]bool, len(dirResponse.Roots))
@ -342,276 +315,47 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q
return nil return nil
} }
// modCacheRegexp splits a path in a module cache into module, module version, and package. // adhocPackage attempts to load or construct an ad-hoc package for a given
var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) // query, if the original call to the driver produced inadequate results.
func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error { response, err := state.createDriverResponse(query)
// calling `go env` isn't free; bail out if there's nothing to do.
if len(queries) == 0 {
return nil
}
// Determine which directories are relevant to scan.
roots, modRoot, err := roots(cfg)
if err != nil { if err != nil {
return err return nil, err
} }
// If we get nothing back from `go list`,
// Scan the selected directories. Simple matches, from GOPATH/GOROOT // try to make this file into its own ad-hoc package.
// or the local module, can simply be "go list"ed. Matches from the // TODO(rstambler): Should this check against the original response?
// module cache need special treatment. if len(response.Packages) == 0 {
var matchesMu sync.Mutex response.Packages = append(response.Packages, &Package{
var simpleMatches, modCacheMatches []string ID: "command-line-arguments",
add := func(root gopathwalk.Root, dir string) { PkgPath: query,
// Walk calls this concurrently; protect the result slices. GoFiles: []string{query},
matchesMu.Lock() CompiledGoFiles: []string{query},
defer matchesMu.Unlock() Imports: make(map[string]*Package),
path := dir
if dir != root.Path {
path = dir[len(root.Path)+1:]
}
if pathMatchesQueries(path, queries) {
switch root.Type {
case gopathwalk.RootModuleCache:
modCacheMatches = append(modCacheMatches, path)
case gopathwalk.RootCurrentModule:
// We'd need to read go.mod to find the full
// import path. Relative's easier.
rel, err := filepath.Rel(cfg.Dir, dir)
if err != nil {
// This ought to be impossible, since
// we found dir in the current module.
panic(err)
}
simpleMatches = append(simpleMatches, "./"+rel)
case gopathwalk.RootGOPATH, gopathwalk.RootGOROOT:
simpleMatches = append(simpleMatches, path)
}
}
}
startWalk := time.Now()
gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug})
cfg.Logf("%v for walk", time.Since(startWalk))
// Weird special case: the top-level package in a module will be in
// whatever directory the user checked the repository out into. It's
// more reasonable for that to not match the package name. So, if there
// are any Go files in the mod root, query it just to be safe.
if modRoot != "" {
rel, err := filepath.Rel(cfg.Dir, modRoot)
if err != nil {
panic(err) // See above.
}
files, err := ioutil.ReadDir(modRoot)
if err != nil {
panic(err) // See above.
}
for _, f := range files {
if strings.HasSuffix(f.Name(), ".go") {
simpleMatches = append(simpleMatches, rel)
break
}
}
}
addResponse := func(r *driverResponse) {
for _, pkg := range r.Packages {
response.addPackage(pkg)
for _, name := range queries {
if pkg.Name == name {
response.addRoot(pkg.ID)
break
}
}
}
}
if len(simpleMatches) != 0 {
resp, err := driver(cfg, simpleMatches...)
if err != nil {
return err
}
addResponse(resp)
}
// Module cache matches are tricky. We want to avoid downloading new
// versions of things, so we need to use the ones present in the cache.
// go list doesn't accept version specifiers, so we have to write out a
// temporary module, and do the list in that module.
if len(modCacheMatches) != 0 {
// Collect all the matches, deduplicating by major version
// and preferring the newest.
type modInfo struct {
mod string
major string
}
mods := make(map[modInfo]string)
var imports []string
for _, modPath := range modCacheMatches {
matches := modCacheRegexp.FindStringSubmatch(modPath)
mod, ver := filepath.ToSlash(matches[1]), matches[2]
importPath := filepath.ToSlash(filepath.Join(matches[1], matches[3]))
major := semver.Major(ver)
if prevVer, ok := mods[modInfo{mod, major}]; !ok || semver.Compare(ver, prevVer) > 0 {
mods[modInfo{mod, major}] = ver
}
imports = append(imports, importPath)
}
// Build the temporary module.
var gomod bytes.Buffer
gomod.WriteString("module modquery\nrequire (\n")
for mod, version := range mods {
gomod.WriteString("\t" + mod.mod + " " + version + "\n")
}
gomod.WriteString(")\n")
tmpCfg := *cfg
// We're only trying to look at stuff in the module cache, so
// disable the network. This should speed things up, and has
// prevented errors in at least one case, #28518.
tmpCfg.Env = append([]string{"GOPROXY=off"}, cfg.Env...)
var err error
tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery")
if err != nil {
return err
}
defer os.RemoveAll(tmpCfg.Dir)
if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil {
return fmt.Errorf("writing go.mod for module cache query: %v", err)
}
// Run the query, using the import paths calculated from the matches above.
resp, err := driver(&tmpCfg, imports...)
if err != nil {
return fmt.Errorf("querying module cache matches: %v", err)
}
addResponse(resp)
}
return nil
}
func getSizes(cfg *Config) (types.Sizes, error) {
return packagesdriver.GetSizesGolist(cfg.Context, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
}
// roots selects the appropriate paths to walk based on the passed-in configuration,
// particularly the environment and the presence of a go.mod in cfg.Dir's parents.
func roots(cfg *Config) ([]gopathwalk.Root, string, error) {
stdout, err := invokeGo(cfg, "env", "GOROOT", "GOPATH", "GOMOD")
if err != nil {
return nil, "", err
}
fields := strings.Split(stdout.String(), "\n")
if len(fields) != 4 || len(fields[3]) != 0 {
return nil, "", fmt.Errorf("go env returned unexpected output: %q", stdout.String())
}
goroot, gopath, gomod := fields[0], filepath.SplitList(fields[1]), fields[2]
var modDir string
if gomod != "" {
modDir = filepath.Dir(gomod)
}
var roots []gopathwalk.Root
// Always add GOROOT.
roots = append(roots, gopathwalk.Root{
Path: filepath.Join(goroot, "/src"),
Type: gopathwalk.RootGOROOT,
})
// If modules are enabled, scan the module dir.
if modDir != "" {
roots = append(roots, gopathwalk.Root{
Path: modDir,
Type: gopathwalk.RootCurrentModule,
}) })
response.Roots = append(response.Roots, "command-line-arguments")
} }
// Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode. // Handle special cases.
for _, p := range gopath { if len(response.Packages) == 1 {
if modDir != "" { // golang/go#33482: If this is a file= query for ad-hoc packages where
roots = append(roots, gopathwalk.Root{ // the file only exists on an overlay, and exists outside of a module,
Path: filepath.Join(p, "/pkg/mod"), // add the file to the package and remove the errors.
Type: gopathwalk.RootModuleCache, if response.Packages[0].ID == "command-line-arguments" ||
}) filepath.ToSlash(response.Packages[0].PkgPath) == filepath.ToSlash(query) {
} else { if len(response.Packages[0].GoFiles) == 0 {
roots = append(roots, gopathwalk.Root{ filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
Path: filepath.Join(p, "/src"), // TODO(matloob): check if the file is outside of a root dir?
Type: gopathwalk.RootGOPATH, for path := range state.cfg.Overlay {
}) if path == filename {
} response.Packages[0].Errors = nil
} response.Packages[0].GoFiles = []string{path}
response.Packages[0].CompiledGoFiles = []string{path}
return roots, modDir, nil }
} }
// These functions were copied from goimports. See further documentation there.
// pathMatchesQueries is adapted from pkgIsCandidate.
// TODO: is it reasonable to do Contains here, rather than an exact match on a path component?
func pathMatchesQueries(path string, queries []string) bool {
lastTwo := lastTwoComponents(path)
for _, query := range queries {
if strings.Contains(lastTwo, query) {
return true
}
if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(query) {
lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
if strings.Contains(lastTwo, query) {
return true
} }
} }
} }
return false return response, nil
}
// 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
}
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)
} }
// Fields must match go list; // Fields must match go list;
@ -656,10 +400,9 @@ func otherFiles(p *jsonPackage) [][]string {
return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles} return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
} }
// golistDriver uses the "go list" command to expand the pattern // createDriverResponse uses the "go list" command to expand the pattern
// words and return metadata for the specified packages. dir may be // words and return a response for the specified packages.
// "" and env may be nil, as per os/exec.Command. func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driverResponse, error) {
// go list uses the following identifiers in ImportPath and Imports: // go list uses the following identifiers in ImportPath and Imports:
// //
// "p" -- importable package or main (command) // "p" -- importable package or main (command)
@ -673,11 +416,13 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
// Run "go list" for complete // Run "go list" for complete
// information on the specified packages. // information on the specified packages.
buf, err := invokeGo(cfg, "list", golistargs(cfg, words)...) buf, err := state.invokeGo("list", golistargs(state.cfg, words)...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
seen := make(map[string]*jsonPackage) seen := make(map[string]*jsonPackage)
pkgs := make(map[string]*Package)
additionalErrors := make(map[string][]Error)
// Decode the JSON and convert it to Package form. // Decode the JSON and convert it to Package form.
var response driverResponse var response driverResponse
for dec := json.NewDecoder(buf); dec.More(); { for dec := json.NewDecoder(buf); dec.More(); {
@ -708,18 +453,72 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
// contained in a known module or GOPATH entry. This will allow the package to be // contained in a known module or GOPATH entry. This will allow the package to be
// properly "reclaimed" when overlays are processed. // properly "reclaimed" when overlays are processed.
if filepath.IsAbs(p.ImportPath) && p.Error != nil { if filepath.IsAbs(p.ImportPath) && p.Error != nil {
pkgPath, ok := getPkgPath(cfg, p.ImportPath, rootsDirs) pkgPath, ok, err := state.getPkgPath(p.ImportPath)
if err != nil {
return nil, err
}
if ok { if ok {
p.ImportPath = pkgPath p.ImportPath = pkgPath
} }
} }
if old, found := seen[p.ImportPath]; found { if old, found := seen[p.ImportPath]; found {
if !reflect.DeepEqual(p, old) { // If one version of the package has an error, and the other doesn't, assume
return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath) // that this is a case where go list is reporting a fake dependency variant
// of the imported package: When a package tries to invalidly import another
// package, go list emits a variant of the imported package (with the same
// import path, but with an error on it, and the package will have a
// DepError set on it). An example of when this can happen is for imports of
// main packages: main packages can not be imported, but they may be
// separately matched and listed by another pattern.
// See golang.org/issue/36188 for more details.
// The plan is that eventually, hopefully in Go 1.15, the error will be
// reported on the importing package rather than the duplicate "fake"
// version of the imported package. Once all supported versions of Go
// have the new behavior this logic can be deleted.
// TODO(matloob): delete the workaround logic once all supported versions of
// Go return the errors on the proper package.
// There should be exactly one version of a package that doesn't have an
// error.
if old.Error == nil && p.Error == nil {
if !reflect.DeepEqual(p, old) {
return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
}
continue
} }
// skip the duplicate
continue // Determine if this package's error needs to be bubbled up.
// This is a hack, and we expect for go list to eventually set the error
// on the package.
if old.Error != nil {
var errkind string
if strings.Contains(old.Error.Err, "not an importable package") {
errkind = "not an importable package"
} else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
errkind = "use of internal package not allowed"
}
if errkind != "" {
if len(old.Error.ImportStack) < 2 {
return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack with fewer than two elements`, errkind)
}
importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-2]
additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
Pos: old.Error.Pos,
Msg: old.Error.Err,
Kind: ListError,
})
}
}
// Make sure that if there's a version of the package without an error,
// that's the one reported to the user.
if old.Error == nil {
continue
}
// This package will replace the old one at the end of the loop.
} }
seen[p.ImportPath] = p seen[p.ImportPath] = p
@ -729,6 +528,7 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles), GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles), CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
OtherFiles: absJoin(p.Dir, otherFiles(p)...), OtherFiles: absJoin(p.Dir, otherFiles(p)...),
forTest: p.ForTest,
} }
// Work around https://golang.org/issue/28749: // Work around https://golang.org/issue/28749:
@ -817,29 +617,37 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
}) })
} }
pkgs[pkg.ID] = pkg
}
for id, errs := range additionalErrors {
if p, ok := pkgs[id]; ok {
p.Errors = append(p.Errors, errs...)
}
}
for _, pkg := range pkgs {
response.Packages = append(response.Packages, pkg) response.Packages = append(response.Packages, pkg)
} }
sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
return &response, nil return &response, nil
} }
// getPkgPath finds the package path of a directory if it's relative to a root directory. // getPkgPath finds the package path of a directory if it's relative to a root directory.
func getPkgPath(cfg *Config, dir string, goInfo func() *goInfo) (string, bool) { func (state *golistState) getPkgPath(dir string) (string, bool, error) {
absDir, err := filepath.Abs(dir) absDir, err := filepath.Abs(dir)
if err != nil { if err != nil {
cfg.Logf("error getting absolute path of %s: %v", dir, err) return "", false, err
return "", false
} }
for rdir, rpath := range goInfo().rootDirs { roots, err := state.determineRootDirs()
absRdir, err := filepath.Abs(rdir) if err != nil {
if err != nil { return "", false, err
cfg.Logf("error getting absolute path of %s: %v", rdir, err) }
continue
} for rdir, rpath := range roots {
// Make sure that the directory is in the module, // Make sure that the directory is in the module,
// to avoid creating a path relative to another module. // to avoid creating a path relative to another module.
if !strings.HasPrefix(absDir, absRdir) { if !strings.HasPrefix(absDir, rdir) {
cfg.Logf("%s does not have prefix %s", absDir, absRdir)
continue continue
} }
// TODO(matloob): This doesn't properly handle symlinks. // TODO(matloob): This doesn't properly handle symlinks.
@ -854,11 +662,11 @@ func getPkgPath(cfg *Config, dir string, goInfo func() *goInfo) (string, bool) {
// Once the file is saved, gopls, or the next invocation of the tool will get the correct // Once the file is saved, gopls, or the next invocation of the tool will get the correct
// result straight from golist. // result straight from golist.
// TODO(matloob): Implement module tiebreaking? // TODO(matloob): Implement module tiebreaking?
return path.Join(rpath, filepath.ToSlash(r)), true return path.Join(rpath, filepath.ToSlash(r)), true, nil
} }
return filepath.ToSlash(r), true return filepath.ToSlash(r), true, nil
} }
return "", false return "", false, nil
} }
// absJoin absolutizes and flattens the lists of files. // absJoin absolutizes and flattens the lists of files.
@ -878,7 +686,7 @@ func golistargs(cfg *Config, words []string) []string {
const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
fullargs := []string{ fullargs := []string{
"-e", "-json", "-e", "-json",
fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypesInfo|NeedTypesSizes) != 0), fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
fmt.Sprintf("-test=%t", cfg.Tests), fmt.Sprintf("-test=%t", cfg.Tests),
fmt.Sprintf("-export=%t", usesExportData(cfg)), fmt.Sprintf("-export=%t", usesExportData(cfg)),
fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0), fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
@ -893,13 +701,17 @@ func golistargs(cfg *Config, words []string) []string {
} }
// invokeGo returns the stdout of a go command invocation. // invokeGo returns the stdout of a go command invocation.
func invokeGo(cfg *Config, verb string, args ...string) (*bytes.Buffer, error) { func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
cfg := state.cfg
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
goArgs := []string{verb} goArgs := []string{verb}
goArgs = append(goArgs, cfg.BuildFlags...) if verb != "env" {
goArgs = append(goArgs, cfg.BuildFlags...)
}
goArgs = append(goArgs, args...) goArgs = append(goArgs, args...)
cmd := exec.CommandContext(cfg.Context, "go", goArgs...) cmd := exec.CommandContext(state.ctx, "go", goArgs...)
// On darwin the cwd gets resolved to the real path, which breaks anything that // 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 // expects the working directory to keep the original path, including the
// go command when dealing with modules. // go command when dealing with modules.
@ -911,7 +723,7 @@ func invokeGo(cfg *Config, verb string, args ...string) (*bytes.Buffer, error) {
cmd.Stdout = stdout cmd.Stdout = stdout
cmd.Stderr = stderr cmd.Stderr = stderr
defer func(start time.Time) { defer func(start time.Time) {
cfg.Logf("%s for %v, stderr: <<%s>> stdout: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr, stdout) cfg.Logf("%s for %v, stderr: <<%s>> stdout: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, goArgs...), stderr, stdout)
}(time.Now()) }(time.Now())
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
@ -951,7 +763,12 @@ func invokeGo(cfg *Config, verb string, args ...string) (*bytes.Buffer, error) {
!strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r) !strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
} }
if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") { if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
if strings.HasPrefix(strings.TrimLeftFunc(stderr.String()[len("# "):], isPkgPathRune), "\n") { msg := stderr.String()[len("# "):]
if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
return stdout, nil
}
// Treat pkg-config errors as a special case (golang.org/issue/36770).
if strings.HasPrefix(msg, "pkg-config") {
return stdout, nil return stdout, nil
} }
} }

View File

@ -1,12 +1,13 @@
package packages package packages
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"go/parser" "go/parser"
"go/token" "go/token"
"os"
"path/filepath" "path/filepath"
"sort"
"strconv" "strconv"
"strings" "strings"
) )
@ -16,7 +17,7 @@ import (
// sometimes incorrect. // sometimes incorrect.
// TODO(matloob): Handle unsupported cases, including the following: // TODO(matloob): Handle unsupported cases, including the following:
// - determining the correct package to add given a new import path // - determining the correct package to add given a new import path
func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func() *goInfo) (modifiedPkgs, needPkgs []string, err error) { func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
havePkgs := make(map[string]string) // importPath -> non-test package ID havePkgs := make(map[string]string) // importPath -> non-test package ID
needPkgsSet := make(map[string]bool) needPkgsSet := make(map[string]bool)
modifiedPkgsSet := make(map[string]bool) modifiedPkgsSet := make(map[string]bool)
@ -34,7 +35,23 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
// potentially modifying the transitive set of dependencies). // potentially modifying the transitive set of dependencies).
var overlayAddsImports bool var overlayAddsImports bool
for opath, contents := range cfg.Overlay { // If both a package and its test package are created by the overlay, we
// need the real package first. Process all non-test files before test
// files, and make the whole process deterministic while we're at it.
var overlayFiles []string
for opath := range state.cfg.Overlay {
overlayFiles = append(overlayFiles, opath)
}
sort.Slice(overlayFiles, func(i, j int) bool {
iTest := strings.HasSuffix(overlayFiles[i], "_test.go")
jTest := strings.HasSuffix(overlayFiles[j], "_test.go")
if iTest != jTest {
return !iTest // non-tests are before tests.
}
return overlayFiles[i] < overlayFiles[j]
})
for _, opath := range overlayFiles {
contents := state.cfg.Overlay[opath]
base := filepath.Base(opath) base := filepath.Base(opath)
dir := filepath.Dir(opath) dir := filepath.Dir(opath)
var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant
@ -64,14 +81,8 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
testVariantOf = p testVariantOf = p
continue nextPackage continue nextPackage
} }
// We must have already seen the package of which this is a test variant.
if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath {
// If we've already seen the test variant,
// make sure to label which package it is a test variant of.
if hasTestFiles(pkg) {
testVariantOf = p
continue nextPackage
}
// If we have already seen the package of which this is a test variant.
if hasTestFiles(p) { if hasTestFiles(p) {
testVariantOf = pkg testVariantOf = pkg
} }
@ -86,7 +97,10 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
if pkg == nil { if pkg == nil {
// Try to find the module or gopath dir the file is contained in. // Try to find the module or gopath dir the file is contained in.
// Then for modules, add the module opath to the beginning. // Then for modules, add the module opath to the beginning.
pkgPath, ok := getPkgPath(cfg, dir, rootDirs) pkgPath, ok, err := state.getPkgPath(dir)
if err != nil {
return nil, nil, err
}
if !ok { if !ok {
break break
} }
@ -114,6 +128,11 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
if isTestFile && !isXTest && testVariantOf != nil { if isTestFile && !isXTest && testVariantOf != nil {
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
// Add the package under test and its imports to the test variant.
pkg.forTest = testVariantOf.PkgPath
for k, v := range testVariantOf.Imports {
pkg.Imports[k] = &Package{ID: v.ID}
}
} }
} }
} }
@ -130,42 +149,45 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
continue continue
} }
for _, imp := range imports { for _, imp := range imports {
_, found := pkg.Imports[imp] if _, found := pkg.Imports[imp]; found {
if !found { continue
overlayAddsImports = true }
// TODO(matloob): Handle cases when the following block isn't correct. overlayAddsImports = true
// These include imports of vendored packages, etc. id, ok := havePkgs[imp]
id, ok := havePkgs[imp] if !ok {
if !ok { var err error
id = imp id, err = state.resolveImport(dir, imp)
} if err != nil {
pkg.Imports[imp] = &Package{ID: id} return nil, nil, err
// Add dependencies to the non-test variant version of this package as wel.
if testVariantOf != nil {
testVariantOf.Imports[imp] = &Package{ID: id}
} }
} }
pkg.Imports[imp] = &Package{ID: id}
// Add dependencies to the non-test variant version of this package as well.
if testVariantOf != nil {
testVariantOf.Imports[imp] = &Package{ID: id}
}
} }
continue
} }
// toPkgPath tries to guess the package path given the id. // toPkgPath guesses the package path given the id.
// This isn't always correct -- it's certainly wrong for toPkgPath := func(sourceDir, id string) (string, error) {
// vendored packages' paths. if i := strings.IndexByte(id, ' '); i >= 0 {
toPkgPath := func(id string) string { return state.resolveImport(sourceDir, id[:i])
// TODO(matloob): Handle vendor paths.
i := strings.IndexByte(id, ' ')
if i >= 0 {
return id[:i]
} }
return id return state.resolveImport(sourceDir, id)
} }
// Do another pass now that new packages have been created to determine the // Now that new packages have been created, do another pass to determine
// set of missing packages. // the new set of missing packages.
for _, pkg := range response.dr.Packages { for _, pkg := range response.dr.Packages {
for _, imp := range pkg.Imports { for _, imp := range pkg.Imports {
pkgPath := toPkgPath(imp.ID) if len(pkg.GoFiles) == 0 {
return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath)
}
pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID)
if err != nil {
return nil, nil, err
}
if _, ok := havePkgs[pkgPath]; !ok { if _, ok := havePkgs[pkgPath]; !ok {
needPkgsSet[pkgPath] = true needPkgsSet[pkgPath] = true
} }
@ -185,6 +207,52 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
return modifiedPkgs, needPkgs, err return modifiedPkgs, needPkgs, err
} }
// resolveImport finds the the ID of a package given its import path.
// In particular, it will find the right vendored copy when in GOPATH mode.
func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) {
env, err := state.getEnv()
if err != nil {
return "", err
}
if env["GOMOD"] != "" {
return importPath, nil
}
searchDir := sourceDir
for {
vendorDir := filepath.Join(searchDir, "vendor")
exists, ok := state.vendorDirs[vendorDir]
if !ok {
info, err := os.Stat(vendorDir)
exists = err == nil && info.IsDir()
state.vendorDirs[vendorDir] = exists
}
if exists {
vendoredPath := filepath.Join(vendorDir, importPath)
if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() {
// We should probably check for .go files here, but shame on anyone who fools us.
path, ok, err := state.getPkgPath(vendoredPath)
if err != nil {
return "", err
}
if ok {
return path, nil
}
}
}
// We know we've hit the top of the filesystem when we Dir / and get /,
// or C:\ and get C:\, etc.
next := filepath.Dir(searchDir)
if next == searchDir {
break
}
searchDir = next
}
return importPath, nil
}
func hasTestFiles(p *Package) bool { func hasTestFiles(p *Package) bool {
for _, f := range p.GoFiles { for _, f := range p.GoFiles {
if strings.HasSuffix(f, "_test.go") { if strings.HasSuffix(f, "_test.go") {
@ -194,44 +262,59 @@ func hasTestFiles(p *Package) bool {
return false return false
} }
// determineRootDirs returns a mapping from directories code can be contained in to the // determineRootDirs returns a mapping from absolute directories that could
// corresponding import path prefixes of those directories. // contain code to their corresponding import path prefixes.
// Its result is used to try to determine the import path for a package containing func (state *golistState) determineRootDirs() (map[string]string, error) {
// an overlay file. env, err := state.getEnv()
func determineRootDirs(cfg *Config) map[string]string {
// Assume modules first:
out, err := invokeGo(cfg, "list", "-m", "-json", "all")
if err != nil { if err != nil {
return determineRootDirsGOPATH(cfg) return nil, err
}
if env["GOMOD"] != "" {
state.rootsOnce.Do(func() {
state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
})
} else {
state.rootsOnce.Do(func() {
state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
})
}
return state.rootDirs, state.rootDirsError
}
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
out, err := state.invokeGo("list", "-m", "-json", "all")
if err != nil {
return nil, err
} }
m := map[string]string{} m := map[string]string{}
type jsonMod struct{ Path, Dir string } type jsonMod struct{ Path, Dir string }
for dec := json.NewDecoder(out); dec.More(); { for dec := json.NewDecoder(out); dec.More(); {
mod := new(jsonMod) mod := new(jsonMod)
if err := dec.Decode(mod); err != nil { if err := dec.Decode(mod); err != nil {
return m // Give up and return an empty map. Package won't be found for overlay. return nil, err
} }
if mod.Dir != "" && mod.Path != "" { if mod.Dir != "" && mod.Path != "" {
// This is a valid module; add it to the map. // This is a valid module; add it to the map.
m[mod.Dir] = mod.Path absDir, err := filepath.Abs(mod.Dir)
if err != nil {
return nil, err
}
m[absDir] = mod.Path
} }
} }
return m return m, nil
} }
func determineRootDirsGOPATH(cfg *Config) map[string]string { func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
m := map[string]string{} m := map[string]string{}
out, err := invokeGo(cfg, "env", "GOPATH") for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
if err != nil { absDir, err := filepath.Abs(dir)
// Could not determine root dir mapping. Everything is best-effort, so just return an empty map. if err != nil {
// When we try to find the import path for a directory, there will be no root-dir match and return nil, err
// we'll give up. }
return m m[filepath.Join(absDir, "src")] = ""
} }
for _, p := range filepath.SplitList(string(bytes.TrimSpace(out.Bytes()))) { return m, nil
m[filepath.Join(p, "src")] = ""
}
return m
} }
func extractImports(filename string, contents []byte) ([]string, error) { func extractImports(filename string, contents []byte) ([]string, error) {

View File

@ -23,6 +23,7 @@ import (
"sync" "sync"
"golang.org/x/tools/go/gcexportdata" "golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/internal/packagesinternal"
) )
// A LoadMode controls the amount of detail to return when loading. // A LoadMode controls the amount of detail to return when loading.
@ -34,6 +35,9 @@ import (
// Load may return more information than requested. // Load may return more information than requested.
type LoadMode int type LoadMode int
// TODO(matloob): When a V2 of go/packages is released, rename NeedExportsFile to
// NeedExportFile to make it consistent with the Package field it's adding.
const ( const (
// NeedName adds Name and PkgPath. // NeedName adds Name and PkgPath.
NeedName LoadMode = 1 << iota NeedName LoadMode = 1 << iota
@ -51,7 +55,7 @@ const (
// NeedDeps adds the fields requested by the LoadMode in the packages in Imports. // NeedDeps adds the fields requested by the LoadMode in the packages in Imports.
NeedDeps NeedDeps
// NeedExportsFile adds ExportsFile. // NeedExportsFile adds ExportFile.
NeedExportsFile NeedExportsFile
// NeedTypes adds Types, Fset, and IllTyped. // NeedTypes adds Types, Fset, and IllTyped.
@ -292,6 +296,15 @@ type Package struct {
// TypesSizes provides the effective size function for types in TypesInfo. // TypesSizes provides the effective size function for types in TypesInfo.
TypesSizes types.Sizes TypesSizes types.Sizes
// forTest is the package under test, if any.
forTest string
}
func init() {
packagesinternal.GetForTest = func(p interface{}) string {
return p.(*Package).forTest
}
} }
// An Error describes a problem with a package's metadata, syntax, or types. // An Error describes a problem with a package's metadata, syntax, or types.
@ -500,12 +513,23 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
if i, found := rootMap[pkg.ID]; found { if i, found := rootMap[pkg.ID]; found {
rootIndex = i rootIndex = i
} }
// Overlays can invalidate export data.
// TODO(matloob): make this check fine-grained based on dependencies on overlaid files
exportDataInvalid := len(ld.Overlay) > 0 || pkg.ExportFile == "" && pkg.PkgPath != "unsafe"
// This package needs type information if the caller requested types and the package is
// either a root, or it's a non-root and the user requested dependencies ...
needtypes := (ld.Mode&NeedTypes|NeedTypesInfo != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0))
// This package needs source if the call requested source (or types info, which implies source)
// and the package is either a root, or itas a non- root and the user requested dependencies...
needsrc := ((ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) ||
// ... or if we need types and the exportData is invalid. We fall back to (incompletely)
// typechecking packages from source if they fail to compile.
(ld.Mode&NeedTypes|NeedTypesInfo != 0 && exportDataInvalid)) && pkg.PkgPath != "unsafe"
lpkg := &loaderPackage{ lpkg := &loaderPackage{
Package: pkg, Package: pkg,
needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0, needtypes: needtypes,
needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0 || needsrc: needsrc,
len(ld.Overlay) > 0 || // Overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files
pkg.ExportFile == "" && pkg.PkgPath != "unsafe",
} }
ld.pkgs[lpkg.ID] = lpkg ld.pkgs[lpkg.ID] = lpkg
if rootIndex >= 0 { if rootIndex >= 0 {

View File

@ -1,196 +0,0 @@
// 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"
)
// TraverseLink is used as a return value from WalkFuncs to indicate that the
// symlink named in the call may be traversed.
var TraverseLink = errors.New("fastwalk: traverse symlink, assuming target is a directory")
// SkipFiles 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 SkipFiles = 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 == TraverseLink {
// 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)
}

View File

@ -1,13 +0,0 @@
// 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.
// +build freebsd openbsd netbsd
package fastwalk
import "syscall"
func direntInode(dirent *syscall.Dirent) uint64 {
return uint64(dirent.Fileno)
}

View File

@ -1,14 +0,0 @@
// 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.
// +build linux darwin
// +build !appengine
package fastwalk
import "syscall"
func direntInode(dirent *syscall.Dirent) uint64 {
return uint64(dirent.Ino)
}

View File

@ -1,13 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd openbsd netbsd
package fastwalk
import "syscall"
func direntNamlen(dirent *syscall.Dirent) uint64 {
return uint64(dirent.Namlen)
}

View File

@ -1,29 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
// +build !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)
}

View File

@ -1,37 +0,0 @@
// 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.
// +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 == SkipFiles {
skipFiles = true
continue
}
return err
}
}
return nil
}

View File

@ -1,127 +0,0 @@
// 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.
// +build linux darwin freebsd openbsd netbsd
// +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 := syscall.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 = syscall.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 == SkipFiles {
skipFiles = true
continue
}
return err
}
}
}
func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) {
// golang.org/issue/15653
dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0]))
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
}

View File

@ -1,273 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package gopathwalk is like filepath.Walk but specialized for finding Go
// packages, particularly in $GOPATH and $GOROOT.
package gopathwalk
import (
"bufio"
"bytes"
"fmt"
"go/build"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"time"
"golang.org/x/tools/internal/fastwalk"
)
// Options controls the behavior of a Walk call.
type Options struct {
Debug bool // Enable debug logging
ModulesEnabled bool // Search module caches. Also disables legacy goimports ignore rules.
}
// 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
}
// SrcDirsRoots returns the roots from build.Default.SrcDirs(). Not modules-compatible.
func SrcDirsRoots(ctx *build.Context) []Root {
var roots []Root
roots = append(roots, Root{filepath.Join(ctx.GOROOT, "src"), RootGOROOT})
for _, p := range filepath.SplitList(ctx.GOPATH) {
roots = append(roots, Root{filepath.Join(p, "src"), RootGOPATH})
}
return roots
}
// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages.
// 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.Debug {
log.Printf("skipping nonexistent directory: %v", root.Path)
}
return
}
start := time.Now()
if opts.Debug {
log.Printf("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.Debug {
log.Printf("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.Debug {
log.Printf("Directory added to ignore list: %s", full)
}
} else if w.opts.Debug {
log.Printf("Error statting ignored directory: %v", err)
}
}
}
// getIgnoredDirs reads an optional config file at <path>/.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.Debug {
if err != nil {
log.Print(err)
} else {
log.Printf("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 {
dir := filepath.Dir(path)
if typ.IsRegular() {
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.SkipFiles
}
if !strings.HasSuffix(path, ".go") {
return nil
}
w.add(w.root, dir)
return fastwalk.SkipFiles
}
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
}
fi, err := os.Lstat(path)
if err != nil {
// Just ignore it.
return nil
}
if w.shouldTraverse(dir, fi) {
return fastwalk.TraverseLink
}
}
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(dir string, fi os.FileInfo) bool {
path := filepath.Join(dir, fi.Name())
target, err := filepath.EvalSymlinks(path)
if err != nil {
return false
}
ts, err := os.Stat(target)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return false
}
if !ts.IsDir() {
return false
}
if w.shouldSkipDir(ts, dir) {
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
}
}

View File

@ -0,0 +1,4 @@
// Package packagesinternal exposes internal-only fields from go/packages.
package packagesinternal
var GetForTest = func(p interface{}) string { return "" }

View File

@ -1,388 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package semver implements comparison of semantic version strings.
// In this package, semantic version strings must begin with a leading "v",
// as in "v1.0.0".
//
// The general form of a semantic version string accepted by this package is
//
// vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
//
// where square brackets indicate optional parts of the syntax;
// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
// using only alphanumeric characters and hyphens; and
// all-numeric PRERELEASE identifiers must not have leading zeros.
//
// This package follows Semantic Versioning 2.0.0 (see semver.org)
// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
package semver
// parsed returns the parsed form of a semantic version string.
type parsed struct {
major string
minor string
patch string
short string
prerelease string
build string
err string
}
// IsValid reports whether v is a valid semantic version string.
func IsValid(v string) bool {
_, ok := parse(v)
return ok
}
// Canonical returns the canonical formatting of the semantic version v.
// It fills in any missing .MINOR or .PATCH and discards build metadata.
// Two semantic versions compare equal only if their canonical formattings
// are identical strings.
// The canonical invalid semantic version is the empty string.
func Canonical(v string) string {
p, ok := parse(v)
if !ok {
return ""
}
if p.build != "" {
return v[:len(v)-len(p.build)]
}
if p.short != "" {
return v + p.short
}
return v
}
// Major returns the major version prefix of the semantic version v.
// For example, Major("v2.1.0") == "v2".
// If v is an invalid semantic version string, Major returns the empty string.
func Major(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
return v[:1+len(pv.major)]
}
// MajorMinor returns the major.minor version prefix of the semantic version v.
// For example, MajorMinor("v2.1.0") == "v2.1".
// If v is an invalid semantic version string, MajorMinor returns the empty string.
func MajorMinor(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
i := 1 + len(pv.major)
if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
return v[:j]
}
return v[:i] + "." + pv.minor
}
// Prerelease returns the prerelease suffix of the semantic version v.
// For example, Prerelease("v2.1.0-pre+meta") == "-pre".
// If v is an invalid semantic version string, Prerelease returns the empty string.
func Prerelease(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
return pv.prerelease
}
// Build returns the build suffix of the semantic version v.
// For example, Build("v2.1.0+meta") == "+meta".
// If v is an invalid semantic version string, Build returns the empty string.
func Build(v string) string {
pv, ok := parse(v)
if !ok {
return ""
}
return pv.build
}
// Compare returns an integer comparing two versions according to
// according to semantic version precedence.
// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
//
// An invalid semantic version string is considered less than a valid one.
// All invalid semantic version strings compare equal to each other.
func Compare(v, w string) int {
pv, ok1 := parse(v)
pw, ok2 := parse(w)
if !ok1 && !ok2 {
return 0
}
if !ok1 {
return -1
}
if !ok2 {
return +1
}
if c := compareInt(pv.major, pw.major); c != 0 {
return c
}
if c := compareInt(pv.minor, pw.minor); c != 0 {
return c
}
if c := compareInt(pv.patch, pw.patch); c != 0 {
return c
}
return comparePrerelease(pv.prerelease, pw.prerelease)
}
// Max canonicalizes its arguments and then returns the version string
// that compares greater.
func Max(v, w string) string {
v = Canonical(v)
w = Canonical(w)
if Compare(v, w) > 0 {
return v
}
return w
}
func parse(v string) (p parsed, ok bool) {
if v == "" || v[0] != 'v' {
p.err = "missing v prefix"
return
}
p.major, v, ok = parseInt(v[1:])
if !ok {
p.err = "bad major version"
return
}
if v == "" {
p.minor = "0"
p.patch = "0"
p.short = ".0.0"
return
}
if v[0] != '.' {
p.err = "bad minor prefix"
ok = false
return
}
p.minor, v, ok = parseInt(v[1:])
if !ok {
p.err = "bad minor version"
return
}
if v == "" {
p.patch = "0"
p.short = ".0"
return
}
if v[0] != '.' {
p.err = "bad patch prefix"
ok = false
return
}
p.patch, v, ok = parseInt(v[1:])
if !ok {
p.err = "bad patch version"
return
}
if len(v) > 0 && v[0] == '-' {
p.prerelease, v, ok = parsePrerelease(v)
if !ok {
p.err = "bad prerelease"
return
}
}
if len(v) > 0 && v[0] == '+' {
p.build, v, ok = parseBuild(v)
if !ok {
p.err = "bad build"
return
}
}
if v != "" {
p.err = "junk on end"
ok = false
return
}
ok = true
return
}
func parseInt(v string) (t, rest string, ok bool) {
if v == "" {
return
}
if v[0] < '0' || '9' < v[0] {
return
}
i := 1
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
i++
}
if v[0] == '0' && i != 1 {
return
}
return v[:i], v[i:], true
}
func parsePrerelease(v string) (t, rest string, ok bool) {
// "A pre-release version MAY be denoted by appending a hyphen and
// a series of dot separated identifiers immediately following the patch version.
// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
if v == "" || v[0] != '-' {
return
}
i := 1
start := 1
for i < len(v) && v[i] != '+' {
if !isIdentChar(v[i]) && v[i] != '.' {
return
}
if v[i] == '.' {
if start == i || isBadNum(v[start:i]) {
return
}
start = i + 1
}
i++
}
if start == i || isBadNum(v[start:i]) {
return
}
return v[:i], v[i:], true
}
func parseBuild(v string) (t, rest string, ok bool) {
if v == "" || v[0] != '+' {
return
}
i := 1
start := 1
for i < len(v) {
if !isIdentChar(v[i]) {
return
}
if v[i] == '.' {
if start == i {
return
}
start = i + 1
}
i++
}
if start == i {
return
}
return v[:i], v[i:], true
}
func isIdentChar(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
}
func isBadNum(v string) bool {
i := 0
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
i++
}
return i == len(v) && i > 1 && v[0] == '0'
}
func isNum(v string) bool {
i := 0
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
i++
}
return i == len(v)
}
func compareInt(x, y string) int {
if x == y {
return 0
}
if len(x) < len(y) {
return -1
}
if len(x) > len(y) {
return +1
}
if x < y {
return -1
} else {
return +1
}
}
func comparePrerelease(x, y string) int {
// "When major, minor, and patch are equal, a pre-release version has
// lower precedence than a normal version.
// Example: 1.0.0-alpha < 1.0.0.
// Precedence for two pre-release versions with the same major, minor,
// and patch version MUST be determined by comparing each dot separated
// identifier from left to right until a difference is found as follows:
// identifiers consisting of only digits are compared numerically and
// identifiers with letters or hyphens are compared lexically in ASCII
// sort order. Numeric identifiers always have lower precedence than
// non-numeric identifiers. A larger set of pre-release fields has a
// higher precedence than a smaller set, if all of the preceding
// identifiers are equal.
// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
if x == y {
return 0
}
if x == "" {
return +1
}
if y == "" {
return -1
}
for x != "" && y != "" {
x = x[1:] // skip - or .
y = y[1:] // skip - or .
var dx, dy string
dx, x = nextIdent(x)
dy, y = nextIdent(y)
if dx != dy {
ix := isNum(dx)
iy := isNum(dy)
if ix != iy {
if ix {
return -1
} else {
return +1
}
}
if ix {
if len(dx) < len(dy) {
return -1
}
if len(dx) > len(dy) {
return +1
}
}
if dx < dy {
return -1
} else {
return +1
}
}
}
if x == "" {
return -1
} else {
return +1
}
}
func nextIdent(x string) (dx, rest string) {
i := 0
for i < len(x) && x[i] != '.' {
i++
}
return x[:i], x[i:]
}

14
vendor/modules.txt vendored
View File

@ -135,10 +135,6 @@ github.com/golang/protobuf/proto
github.com/golang/protobuf/protoc-gen-go/descriptor github.com/golang/protobuf/protoc-gen-go/descriptor
# github.com/golang/snappy v0.0.1 # github.com/golang/snappy v0.0.1
github.com/golang/snappy github.com/golang/snappy
# github.com/gomarkdown/markdown v0.0.0-20191209105822-e3ba6c6109ba => github.com/status-im/markdown v0.0.0-20191209105822-e3ba6c6109ba
github.com/gomarkdown/markdown
github.com/gomarkdown/markdown/ast
github.com/gomarkdown/markdown/parser
# github.com/google/uuid v1.1.1 # github.com/google/uuid v1.1.1
github.com/google/uuid github.com/google/uuid
# github.com/gorilla/websocket v1.4.1 # github.com/gorilla/websocket v1.4.1
@ -362,6 +358,10 @@ github.com/status-im/doubleratchet
github.com/status-im/go-multiaddr-ethv4 github.com/status-im/go-multiaddr-ethv4
# github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e # github.com/status-im/keycard-go v0.0.0-20200107115650-f38e9a19958e
github.com/status-im/keycard-go/derivationpath github.com/status-im/keycard-go/derivationpath
# github.com/status-im/markdown v0.0.0-20200210164614-b9fe92168122
github.com/status-im/markdown
github.com/status-im/markdown/ast
github.com/status-im/markdown/parser
# github.com/status-im/migrate/v4 v4.6.2-status.2 # github.com/status-im/migrate/v4 v4.6.2-status.2
github.com/status-im/migrate/v4 github.com/status-im/migrate/v4
github.com/status-im/migrate/v4/database/postgres github.com/status-im/migrate/v4/database/postgres
@ -515,7 +515,7 @@ golang.org/x/text/secure/bidirule
golang.org/x/text/transform golang.org/x/text/transform
golang.org/x/text/unicode/bidi golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm golang.org/x/text/unicode/norm
# golang.org/x/tools v0.0.0-20200116062425-473961ec044c # golang.org/x/tools v0.0.0-20200211045251-2de505fc5306
golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/passes/inspect golang.org/x/tools/go/analysis/passes/inspect
golang.org/x/tools/go/ast/astutil golang.org/x/tools/go/ast/astutil
@ -527,9 +527,7 @@ golang.org/x/tools/go/internal/packagesdriver
golang.org/x/tools/go/packages golang.org/x/tools/go/packages
golang.org/x/tools/go/types/objectpath golang.org/x/tools/go/types/objectpath
golang.org/x/tools/go/types/typeutil golang.org/x/tools/go/types/typeutil
golang.org/x/tools/internal/fastwalk golang.org/x/tools/internal/packagesinternal
golang.org/x/tools/internal/gopathwalk
golang.org/x/tools/internal/semver
# gopkg.in/go-playground/validator.v9 v9.31.0 # gopkg.in/go-playground/validator.v9 v9.31.0
gopkg.in/go-playground/validator.v9 gopkg.in/go-playground/validator.v9
# gopkg.in/natefinch/lumberjack.v2 v2.0.0 # gopkg.in/natefinch/lumberjack.v2 v2.0.0