├── .gitignore
├── vendor
├── golang.org
│ └── x
│ │ └── tools
│ │ ├── go
│ │ └── ast
│ │ │ └── astutil
│ │ │ ├── util.go
│ │ │ ├── LICENSE
│ │ │ ├── imports.go
│ │ │ └── enclosing.go
│ │ ├── cmd
│ │ └── goimports
│ │ │ ├── goimports_not_gc.go
│ │ │ ├── goimports_gc.go
│ │ │ ├── LICENSE
│ │ │ ├── doc.go
│ │ │ └── goimports.go
│ │ ├── imports
│ │ ├── fastwalk_dirent_ino.go
│ │ ├── fastwalk_dirent_fileno.go
│ │ ├── fastwalk_portable.go
│ │ ├── LICENSE
│ │ ├── mkstdlib.go
│ │ ├── fastwalk_unix.go
│ │ ├── mkindex.go
│ │ ├── fastwalk.go
│ │ ├── sortimports.go
│ │ ├── imports.go
│ │ └── fix.go
│ │ └── cover
│ │ ├── LICENSE
│ │ └── profile.go
├── github.com
│ └── wadey
│ │ └── gocovmerge
│ │ ├── LICENSE
│ │ └── gocovmerge.go
└── manifest
├── public
├── ie10-viewport-bug-workaround.css
├── sticky-footer.css
├── ie10-viewport-bug-workaround.js
├── ie-emulation-modes-warning.js
└── index.html
├── .github
└── workflows
│ └── semgrep.yml
├── README.md
├── main.go
└── Makefile
/.gitignore:
--------------------------------------------------------------------------------
1 | /.GOPATH
2 | /bin
3 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/go/ast/astutil/util.go:
--------------------------------------------------------------------------------
1 | package astutil
2 |
3 | import "go/ast"
4 |
5 | // Unparen returns e with any enclosing parentheses stripped.
6 | func Unparen(e ast.Expr) ast.Expr {
7 | for {
8 | p, ok := e.(*ast.ParenExpr)
9 | if !ok {
10 | return e
11 | }
12 | e = p.X
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/cmd/goimports/goimports_not_gc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build !gc
6 |
7 | package main
8 |
9 | func doTrace() func() {
10 | return func() {}
11 | }
12 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/fastwalk_dirent_ino.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build linux,!appengine darwin
6 |
7 | package imports
8 |
9 | import "syscall"
10 |
11 | func direntInode(dirent *syscall.Dirent) uint64 {
12 | return uint64(dirent.Ino)
13 | }
14 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/fastwalk_dirent_fileno.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build freebsd openbsd netbsd
6 |
7 | package imports
8 |
9 | import "syscall"
10 |
11 | func direntInode(dirent *syscall.Dirent) uint64 {
12 | return uint64(dirent.Fileno)
13 | }
14 |
--------------------------------------------------------------------------------
/public/ie10-viewport-bug-workaround.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * IE10 viewport hack for Surface/desktop Windows 8 bug
3 | * Copyright 2014-2015 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | /*
8 | * See the Getting Started docs for more information:
9 | * http://getbootstrap.com/getting-started/#support-ie10-width
10 | */
11 | @-ms-viewport { width: device-width; }
12 | @-o-viewport { width: device-width; }
13 | @viewport { width: device-width; }
14 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/cmd/goimports/goimports_gc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build gc
6 |
7 | package main
8 |
9 | import (
10 | "flag"
11 | "runtime/trace"
12 | )
13 |
14 | var traceProfile = flag.String("trace", "", "trace profile output")
15 |
16 | func doTrace() func() {
17 | if *traceProfile != "" {
18 | bw, flush := bufferedFileWriter(*traceProfile)
19 | trace.Start(bw)
20 | return func() {
21 | flush()
22 | trace.Stop()
23 | }
24 | }
25 | return func() {}
26 | }
27 |
--------------------------------------------------------------------------------
/.github/workflows/semgrep.yml:
--------------------------------------------------------------------------------
1 |
2 | on:
3 | pull_request: {}
4 | workflow_dispatch: {}
5 | push:
6 | branches:
7 | - main
8 | - master
9 | schedule:
10 | - cron: '0 0 * * *'
11 | name: Semgrep config
12 | jobs:
13 | semgrep:
14 | name: semgrep/ci
15 | runs-on: ubuntu-20.04
16 | env:
17 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
18 | SEMGREP_URL: https://cloudflare.semgrep.dev
19 | SEMGREP_APP_URL: https://cloudflare.semgrep.dev
20 | SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
21 | container:
22 | image: returntocorp/semgrep
23 | steps:
24 | - uses: actions/checkout@v3
25 | - run: semgrep ci
26 |
--------------------------------------------------------------------------------
/public/sticky-footer.css:
--------------------------------------------------------------------------------
1 | /* Sticky footer styles
2 | -------------------------------------------------- */
3 | html {
4 | position: relative;
5 | min-height: 100%;
6 | }
7 | body {
8 | /* Margin bottom by footer height */
9 | margin-bottom: 60px;
10 | }
11 | .footer {
12 | position: absolute;
13 | bottom: 0;
14 | width: 100%;
15 | /* Set the fixed height of the footer here */
16 | height: 60px;
17 | background-color: #f5f5f5;
18 | }
19 |
20 |
21 | /* Custom page CSS
22 | -------------------------------------------------- */
23 | /* Not required for template or sticky footer method. */
24 |
25 | .container {
26 | width: auto;
27 | max-width: 680px;
28 | padding: 0 15px;
29 | }
30 | .container .text-muted {
31 | margin: 20px 0;
32 | }
33 |
--------------------------------------------------------------------------------
/public/ie10-viewport-bug-workaround.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * IE10 viewport hack for Surface/desktop Windows 8 bug
3 | * Copyright 2014-2015 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | // See the Getting Started docs for more information:
8 | // http://getbootstrap.com/getting-started/#support-ie10-width
9 |
10 | (function () {
11 | 'use strict';
12 |
13 | if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
14 | var msViewportStyle = document.createElement('style')
15 | msViewportStyle.appendChild(
16 | document.createTextNode(
17 | '@-ms-viewport{width:auto!important}'
18 | )
19 | )
20 | document.querySelector('head').appendChild(msViewportStyle)
21 | }
22 |
23 | })();
24 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/fastwalk_portable.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd
6 |
7 | package imports
8 |
9 | import (
10 | "io/ioutil"
11 | "os"
12 | )
13 |
14 | // readDir calls fn for each directory entry in dirName.
15 | // It does not descend into directories or follow symlinks.
16 | // If fn returns a non-nil error, readDir returns with that error
17 | // immediately.
18 | func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
19 | fis, err := ioutil.ReadDir(dirName)
20 | if err != nil {
21 | return err
22 | }
23 | for _, fi := range fis {
24 | if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil {
25 | return err
26 | }
27 | }
28 | return nil
29 | }
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | badupnp
2 | ===
3 |
4 | This is a simple web service that tests if the viewer is running a
5 | router that is responding to UPnP / SSDP on the internet. This is
6 | bad because UPnP returns many more packets than the requesting system
7 | sends it, combined with UDP as a transport, it makes it a protocol ripe
8 | for abuse for DDoS reflection.
9 |
10 | This tool was written for the Cloudflare blog post:
11 | https://blog.cloudflare.com/ssdp-100gbps/
12 |
13 | # Example systemd units:
14 |
15 | ```
16 | [Service]
17 | ExecStart=/usr/bin/badupnp
18 | Restart=always
19 | StandardOutput=syslog
20 | StandardError=syslog
21 | SyslogIdentifier=badupnp
22 | User=root
23 | WorkingDirectory=/usr/local/badupnp/
24 | RestartSec=10s
25 |
26 | [Install]
27 | WantedBy=multi-user.target
28 | ```
29 |
30 | # Example lighttpd reverse proxy info:
31 |
32 | ```
33 | # ensure you have mod_proxy loaded
34 |
35 | $HTTP["host"] == "badupnp.benjojo.co.uk" {
36 | proxy.server = ( "" => ( (
37 | "host" => "127.0.0.1",
38 | "port" => 753
39 | ) ) )
40 | }
41 | ```
42 |
--------------------------------------------------------------------------------
/vendor/github.com/wadey/gocovmerge/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Wade Simmons
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/vendor/manifest:
--------------------------------------------------------------------------------
1 | {
2 | "version": 0,
3 | "dependencies": [
4 | {
5 | "importpath": "github.com/wadey/gocovmerge",
6 | "repository": "https://github.com/wadey/gocovmerge",
7 | "vcs": "git",
8 | "revision": "b5bfa59ec0adc420475f97f89b58045c721d761c",
9 | "branch": "master",
10 | "notests": true
11 | },
12 | {
13 | "importpath": "golang.org/x/tools/cmd/goimports",
14 | "repository": "https://go.googlesource.com/tools",
15 | "vcs": "git",
16 | "revision": "e6cb469339aef5b7be0c89de730d5f3cc8e47e50",
17 | "branch": "master",
18 | "path": "/cmd/goimports",
19 | "notests": true
20 | },
21 | {
22 | "importpath": "golang.org/x/tools/cover",
23 | "repository": "https://go.googlesource.com/tools",
24 | "vcs": "git",
25 | "revision": "e6cb469339aef5b7be0c89de730d5f3cc8e47e50",
26 | "branch": "master",
27 | "path": "/cover",
28 | "notests": true
29 | },
30 | {
31 | "importpath": "golang.org/x/tools/go/ast/astutil",
32 | "repository": "https://go.googlesource.com/tools",
33 | "vcs": "git",
34 | "revision": "e6cb469339aef5b7be0c89de730d5f3cc8e47e50",
35 | "branch": "master",
36 | "path": "go/ast/astutil",
37 | "notests": true
38 | },
39 | {
40 | "importpath": "golang.org/x/tools/imports",
41 | "repository": "https://go.googlesource.com/tools",
42 | "vcs": "git",
43 | "revision": "e6cb469339aef5b7be0c89de730d5f3cc8e47e50",
44 | "branch": "master",
45 | "path": "imports",
46 | "notests": true
47 | }
48 | ]
49 | }
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/cover/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 The Go Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 The Go Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/cmd/goimports/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 The Go Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/go/ast/astutil/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 The Go Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/cmd/goimports/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Command goimports updates your Go import lines,
4 | adding missing ones and removing unreferenced ones.
5 |
6 | $ go get golang.org/x/tools/cmd/goimports
7 |
8 | In addition to fixing imports, goimports also formats
9 | your code in the same style as gofmt so it can be used
10 | as a replacement for your editor's gofmt-on-save hook.
11 |
12 | For emacs, make sure you have the latest go-mode.el:
13 | https://github.com/dominikh/go-mode.el
14 | Then in your .emacs file:
15 | (setq gofmt-command "goimports")
16 | (add-to-list 'load-path "/home/you/somewhere/emacs/")
17 | (require 'go-mode-autoloads)
18 | (add-hook 'before-save-hook 'gofmt-before-save)
19 |
20 | For vim, set "gofmt_command" to "goimports":
21 | https://golang.org/change/39c724dd7f252
22 | https://golang.org/wiki/IDEsAndTextEditorPlugins
23 | etc
24 |
25 | For GoSublime, follow the steps described here:
26 | http://michaelwhatcott.com/gosublime-goimports/
27 |
28 | For other editors, you probably know what to do.
29 |
30 | To exclude directories in your $GOPATH from being scanned for Go
31 | files, goimports respects a configuration file at
32 | $GOPATH/src/.goimportsignore which may contain blank lines, comment
33 | lines (beginning with '#'), or lines naming a directory relative to
34 | the configuration file to ignore when scanning. No globbing or regex
35 | patterns are allowed. Use the "-v" verbose flag to verify it's
36 | working and see what goimports is doing.
37 |
38 | File bugs or feature requests at:
39 |
40 | https://golang.org/issues/new?title=x/tools/cmd/goimports:+
41 |
42 | Happy hacking!
43 |
44 | */
45 | package main // import "golang.org/x/tools/cmd/goimports"
46 |
--------------------------------------------------------------------------------
/public/ie-emulation-modes-warning.js:
--------------------------------------------------------------------------------
1 | // NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
2 | // IT'S JUST JUNK FOR OUR DOCS!
3 | // ++++++++++++++++++++++++++++++++++++++++++
4 | /*!
5 | * Copyright 2014-2015 Twitter, Inc.
6 | *
7 | * Licensed under the Creative Commons Attribution 3.0 Unported License. For
8 | * details, see https://creativecommons.org/licenses/by/3.0/.
9 | */
10 | // Intended to prevent false-positive bug reports about Bootstrap not working properly in old versions of IE due to folks testing using IE's unreliable emulation modes.
11 | (function () {
12 | 'use strict';
13 |
14 | function emulatedIEMajorVersion() {
15 | var groups = /MSIE ([0-9.]+)/.exec(window.navigator.userAgent)
16 | if (groups === null) {
17 | return null
18 | }
19 | var ieVersionNum = parseInt(groups[1], 10)
20 | var ieMajorVersion = Math.floor(ieVersionNum)
21 | return ieMajorVersion
22 | }
23 |
24 | function actualNonEmulatedIEMajorVersion() {
25 | // Detects the actual version of IE in use, even if it's in an older-IE emulation mode.
26 | // IE JavaScript conditional compilation docs: https://msdn.microsoft.com/library/121hztk3%28v=vs.94%29.aspx
27 | // @cc_on docs: https://msdn.microsoft.com/library/8ka90k2e%28v=vs.94%29.aspx
28 | var jscriptVersion = new Function('/*@cc_on return @_jscript_version; @*/')() // jshint ignore:line
29 | if (jscriptVersion === undefined) {
30 | return 11 // IE11+ not in emulation mode
31 | }
32 | if (jscriptVersion < 9) {
33 | return 8 // IE8 (or lower; haven't tested on IE<8)
34 | }
35 | return jscriptVersion // IE9 or IE10 in any mode, or IE11 in non-IE11 mode
36 | }
37 |
38 | var ua = window.navigator.userAgent
39 | if (ua.indexOf('Opera') > -1 || ua.indexOf('Presto') > -1) {
40 | return // Opera, which might pretend to be IE
41 | }
42 | var emulated = emulatedIEMajorVersion()
43 | if (emulated === null) {
44 | return // Not IE
45 | }
46 | var nonEmulated = actualNonEmulatedIEMajorVersion()
47 |
48 | if (emulated !== nonEmulated) {
49 | window.alert('WARNING: You appear to be using IE' + nonEmulated + ' in IE' + emulated + ' emulation mode.\nIE emulation modes can behave significantly differently from ACTUAL older versions of IE.\nPLEASE DON\'T FILE BOOTSTRAP BUGS based on testing in IE emulation modes!')
50 | }
51 | })();
52 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Bad UPnP/SSDP - Check for WAN UPnP listening
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
Please enable javscript for this test to work...
32 |
Why is listening for UPnP/SSDP on WAN bad? Cloudflare wrote a blog post that you may find interesting here
33 |
34 |
35 |
40 |
41 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/mkstdlib.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | // mkstdlib generates the zstdlib.go file, containing the Go standard
4 | // library API symbols. It's baked into the binary to avoid scanning
5 | // GOPATH in the common case.
6 | package main
7 |
8 | import (
9 | "bufio"
10 | "bytes"
11 | "fmt"
12 | "go/format"
13 | "io"
14 | "io/ioutil"
15 | "log"
16 | "os"
17 | "path"
18 | "path/filepath"
19 | "regexp"
20 | "sort"
21 | "strings"
22 | )
23 |
24 | func mustOpen(name string) io.Reader {
25 | f, err := os.Open(name)
26 | if err != nil {
27 | log.Fatal(err)
28 | }
29 | return f
30 | }
31 |
32 | func api(base string) string {
33 | return filepath.Join(os.Getenv("GOROOT"), "api", base)
34 | }
35 |
36 | var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`)
37 |
38 | func main() {
39 | var buf bytes.Buffer
40 | outf := func(format string, args ...interface{}) {
41 | fmt.Fprintf(&buf, format, args...)
42 | }
43 | outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n")
44 | outf("package imports\n")
45 | outf("var stdlib = map[string]string{\n")
46 | f := io.MultiReader(
47 | mustOpen(api("go1.txt")),
48 | mustOpen(api("go1.1.txt")),
49 | mustOpen(api("go1.2.txt")),
50 | mustOpen(api("go1.3.txt")),
51 | mustOpen(api("go1.4.txt")),
52 | mustOpen(api("go1.5.txt")),
53 | mustOpen(api("go1.6.txt")),
54 | mustOpen(api("go1.7.txt")),
55 | mustOpen(api("go1.8.txt")),
56 | )
57 | sc := bufio.NewScanner(f)
58 | fullImport := map[string]string{} // "zip.NewReader" => "archive/zip"
59 | ambiguous := map[string]bool{}
60 | var keys []string
61 | for sc.Scan() {
62 | l := sc.Text()
63 | has := func(v string) bool { return strings.Contains(l, v) }
64 | if has("struct, ") || has("interface, ") || has(", method (") {
65 | continue
66 | }
67 | if m := sym.FindStringSubmatch(l); m != nil {
68 | full := m[1]
69 | key := path.Base(full) + "." + m[2]
70 | if exist, ok := fullImport[key]; ok {
71 | if exist != full {
72 | ambiguous[key] = true
73 | }
74 | } else {
75 | fullImport[key] = full
76 | keys = append(keys, key)
77 | }
78 | }
79 | }
80 | if err := sc.Err(); err != nil {
81 | log.Fatal(err)
82 | }
83 | sort.Strings(keys)
84 | for _, key := range keys {
85 | if ambiguous[key] {
86 | outf("\t// %q is ambiguous\n", key)
87 | } else {
88 | outf("\t%q: %q,\n", key, fullImport[key])
89 | }
90 | }
91 | outf("\n")
92 | for _, sym := range [...]string{"Alignof", "ArbitraryType", "Offsetof", "Pointer", "Sizeof"} {
93 | outf("\t%q: %q,\n", "unsafe."+sym, "unsafe")
94 | }
95 | outf("}\n")
96 | fmtbuf, err := format.Source(buf.Bytes())
97 | if err != nil {
98 | log.Fatal(err)
99 | }
100 | err = ioutil.WriteFile("zstdlib.go", fmtbuf, 0666)
101 | if err != nil {
102 | log.Fatal(err)
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/vendor/github.com/wadey/gocovmerge/gocovmerge.go:
--------------------------------------------------------------------------------
1 | // gocovmerge takes the results from multiple `go test -coverprofile` runs and
2 | // merges them into one profile
3 | package main
4 |
5 | import (
6 | "flag"
7 | "fmt"
8 | "io"
9 | "log"
10 | "os"
11 | "sort"
12 |
13 | "golang.org/x/tools/cover"
14 | )
15 |
16 | func mergeProfiles(p *cover.Profile, merge *cover.Profile) {
17 | if p.Mode != merge.Mode {
18 | log.Fatalf("cannot merge profiles with different modes")
19 | }
20 | // Since the blocks are sorted, we can keep track of where the last block
21 | // was inserted and only look at the blocks after that as targets for merge
22 | startIndex := 0
23 | for _, b := range merge.Blocks {
24 | startIndex = mergeProfileBlock(p, b, startIndex)
25 | }
26 | }
27 |
28 | func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int {
29 | sortFunc := func(i int) bool {
30 | pi := p.Blocks[i+startIndex]
31 | return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
32 | }
33 |
34 | i := 0
35 | if sortFunc(i) != true {
36 | i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
37 | }
38 | i += startIndex
39 | if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
40 | if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
41 | log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb)
42 | }
43 | switch p.Mode {
44 | case "set":
45 | p.Blocks[i].Count |= pb.Count
46 | case "count", "atomic":
47 | p.Blocks[i].Count += pb.Count
48 | default:
49 | log.Fatalf("unsupported covermode: '%s'", p.Mode)
50 | }
51 | } else {
52 | if i > 0 {
53 | pa := p.Blocks[i-1]
54 | if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) {
55 | log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb)
56 | }
57 | }
58 | if i < len(p.Blocks)-1 {
59 | pa := p.Blocks[i+1]
60 | if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
61 | log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb)
62 | }
63 | }
64 | p.Blocks = append(p.Blocks, cover.ProfileBlock{})
65 | copy(p.Blocks[i+1:], p.Blocks[i:])
66 | p.Blocks[i] = pb
67 | }
68 | return i + 1
69 | }
70 |
71 | func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
72 | i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
73 | if i < len(profiles) && profiles[i].FileName == p.FileName {
74 | mergeProfiles(profiles[i], p)
75 | } else {
76 | profiles = append(profiles, nil)
77 | copy(profiles[i+1:], profiles[i:])
78 | profiles[i] = p
79 | }
80 | return profiles
81 | }
82 |
83 | func dumpProfiles(profiles []*cover.Profile, out io.Writer) {
84 | if len(profiles) == 0 {
85 | return
86 | }
87 | fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode)
88 | for _, p := range profiles {
89 | for _, b := range p.Blocks {
90 | fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count)
91 | }
92 | }
93 | }
94 |
95 | func main() {
96 | flag.Parse()
97 |
98 | var merged []*cover.Profile
99 |
100 | for _, file := range flag.Args() {
101 | profiles, err := cover.ParseProfiles(file)
102 | if err != nil {
103 | log.Fatalf("failed to parse profiles: %v", err)
104 | }
105 | for _, p := range profiles {
106 | merged = addProfile(merged, p)
107 | }
108 | }
109 |
110 | dumpProfiles(merged, os.Stdout)
111 | }
112 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/fastwalk_unix.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build linux,!appengine darwin freebsd openbsd netbsd
6 |
7 | package imports
8 |
9 | import (
10 | "bytes"
11 | "fmt"
12 | "os"
13 | "syscall"
14 | "unsafe"
15 | )
16 |
17 | const blockSize = 8 << 10
18 |
19 | // unknownFileMode is a sentinel (and bogus) os.FileMode
20 | // value used to represent a syscall.DT_UNKNOWN Dirent.Type.
21 | const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
22 |
23 | func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
24 | fd, err := syscall.Open(dirName, 0, 0)
25 | if err != nil {
26 | return err
27 | }
28 | defer syscall.Close(fd)
29 |
30 | // The buffer must be at least a block long.
31 | buf := make([]byte, blockSize) // stack-allocated; doesn't escape
32 | bufp := 0 // starting read position in buf
33 | nbuf := 0 // end valid data in buf
34 | for {
35 | if bufp >= nbuf {
36 | bufp = 0
37 | nbuf, err = syscall.ReadDirent(fd, buf)
38 | if err != nil {
39 | return os.NewSyscallError("readdirent", err)
40 | }
41 | if nbuf <= 0 {
42 | return nil
43 | }
44 | }
45 | consumed, name, typ := parseDirEnt(buf[bufp:nbuf])
46 | bufp += consumed
47 | if name == "" || name == "." || name == ".." {
48 | continue
49 | }
50 | // Fallback for filesystems (like old XFS) that don't
51 | // support Dirent.Type and have DT_UNKNOWN (0) there
52 | // instead.
53 | if typ == unknownFileMode {
54 | fi, err := os.Lstat(dirName + "/" + name)
55 | if err != nil {
56 | // It got deleted in the meantime.
57 | if os.IsNotExist(err) {
58 | continue
59 | }
60 | return err
61 | }
62 | typ = fi.Mode() & os.ModeType
63 | }
64 | if err := fn(dirName, name, typ); err != nil {
65 | return err
66 | }
67 | }
68 | }
69 |
70 | func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) {
71 | // golang.org/issue/15653
72 | dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0]))
73 | if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v {
74 | panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v))
75 | }
76 | if len(buf) < int(dirent.Reclen) {
77 | panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen))
78 | }
79 | consumed = int(dirent.Reclen)
80 | if direntInode(dirent) == 0 { // File absent in directory.
81 | return
82 | }
83 | switch dirent.Type {
84 | case syscall.DT_REG:
85 | typ = 0
86 | case syscall.DT_DIR:
87 | typ = os.ModeDir
88 | case syscall.DT_LNK:
89 | typ = os.ModeSymlink
90 | case syscall.DT_BLK:
91 | typ = os.ModeDevice
92 | case syscall.DT_FIFO:
93 | typ = os.ModeNamedPipe
94 | case syscall.DT_SOCK:
95 | typ = os.ModeSocket
96 | case syscall.DT_UNKNOWN:
97 | typ = unknownFileMode
98 | default:
99 | // Skip weird things.
100 | // It's probably a DT_WHT (http://lwn.net/Articles/325369/)
101 | // or something. Revisit if/when this package is moved outside
102 | // of goimports. goimports only cares about regular files,
103 | // symlinks, and directories.
104 | return
105 | }
106 |
107 | nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
108 | nameLen := bytes.IndexByte(nameBuf[:], 0)
109 | if nameLen < 0 {
110 | panic("failed to find terminating 0 byte in dirent")
111 | }
112 |
113 | // Special cases for common things:
114 | if nameLen == 1 && nameBuf[0] == '.' {
115 | name = "."
116 | } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' {
117 | name = ".."
118 | } else {
119 | name = string(nameBuf[:nameLen])
120 | }
121 | return
122 | }
123 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "net"
7 | "net/http"
8 | "os"
9 | "regexp"
10 | "strings"
11 | "sync"
12 | "time"
13 | )
14 |
15 | // A web version of Marek's script
16 |
17 | var (
18 | SSDP_SEARCH_P = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 1\r\nST: %s\r\n\r\n"
19 | SERVER_RE = regexp.MustCompile("(?i)server: ?([^\r\n]*)[\r\n]")
20 | ST_RE = regexp.MustCompile("(?i)st: ?([^\r\n]*)[\r\n]")
21 |
22 | listenAddr string
23 | httplistenAddr string
24 | protocol string
25 | ssdpST string
26 | conn *net.UDPConn
27 | nrequestchan chan notifyRequest
28 | )
29 |
30 | func init() {
31 | flag.StringVar(&listenAddr, "udplisten", "0.0.0.0:0", "Listen address")
32 | flag.StringVar(&httplistenAddr, "httplisten", "0.0.0.0:753", "Listen address")
33 | flag.StringVar(&protocol, "p", "udp", "udp/udp4/udp6")
34 | flag.StringVar(&ssdpST, "s", "ssdp:all", "ssdp ST")
35 | }
36 |
37 | func main() {
38 | flag.Parse()
39 |
40 | addr, err := net.ResolveUDPAddr(protocol, listenAddr)
41 | if err != nil {
42 | panic(err)
43 | }
44 |
45 | conn, err = net.ListenUDP(protocol, addr)
46 | if err != nil {
47 | panic(err)
48 | }
49 |
50 | fs := http.FileServer(http.Dir("public"))
51 | http.Handle("/", fs)
52 |
53 | http.HandleFunc("/test", testClient)
54 |
55 | nrequestchan = make(chan notifyRequest)
56 | go resultCollector(nrequestchan)
57 |
58 | fmt.Printf("Failed to start, %s", http.ListenAndServe(httplistenAddr, nil).Error())
59 | }
60 |
61 | type notifyRequest struct {
62 | ip string
63 | c chan bool
64 | }
65 |
66 | func testClient(w http.ResponseWriter, rw *http.Request) {
67 | SSDP_SEARCH := []byte(fmt.Sprintf(SSDP_SEARCH_P, ssdpST))
68 |
69 | ipaddr := rw.Header.Get("CF-Connecting-IP")
70 | if strings.Contains(ipaddr, ":") {
71 | ipaddr = fmt.Sprintf("[%s]", ipaddr)
72 | }
73 |
74 | addr, err := net.ResolveUDPAddr(protocol, fmt.Sprintf("%s:1900", ipaddr))
75 | if err != nil {
76 | fmt.Fprintf(os.Stderr, "[ ] Can't resolve %s\n", ipaddr)
77 | http.Error(w, "Unable to resolve IP", http.StatusInternalServerError)
78 | return
79 | }
80 |
81 | notifyChan := make(chan bool)
82 | request := notifyRequest{
83 | ip: rw.Header.Get("CF-Connecting-IP"),
84 | c: notifyChan,
85 | }
86 |
87 | _, err = conn.WriteToUDP(SSDP_SEARCH, addr)
88 | if err != nil {
89 | fmt.Fprintf(os.Stderr, "[ ] write(%s) failed: %s\n", rw.Header.Get("CF-Connecting-IP"), err)
90 | http.Error(w, "Unable to produce packet", http.StatusInternalServerError)
91 | return
92 | }
93 | nrequestchan <- request
94 |
95 | select {
96 | case <-notifyChan:
97 | w.Write([]byte(`{"result":true}`))
98 | case <-time.After(time.Second * 5):
99 | w.Write([]byte(`{"result":false}`))
100 | }
101 |
102 | close(notifyChan)
103 | }
104 |
105 | func resultCollector(notifyrequests chan notifyRequest) {
106 |
107 | waiting := make(map[string]notifyRequest)
108 | requestlock := sync.Mutex{}
109 |
110 | go func() {
111 | for r := range notifyrequests {
112 | requestlock.Lock()
113 |
114 | waiting[r.ip] = r
115 |
116 | requestlock.Unlock()
117 | }
118 | }()
119 |
120 | for {
121 | var buf [2048]byte
122 | n, remote, err := conn.ReadFromUDP(buf[:])
123 | if err != nil {
124 | fmt.Fprintf(os.Stderr, "[ ] read(): %s\n", err)
125 | continue
126 | }
127 | data := buf[:n]
128 | m := SERVER_RE.FindAllSubmatch(data, -1)
129 | server := []byte{}
130 | if len(m) > 0 && len(m[0]) > 1 {
131 | server = m[0][1]
132 | }
133 |
134 | m = ST_RE.FindAllSubmatch(data, -1)
135 | st := []byte{}
136 | if len(m) > 0 && len(m[0]) > 1 {
137 | st = m[0][1]
138 | }
139 |
140 | sport := 0
141 | if remote.Port == 1900 {
142 | sport = 1
143 | }
144 | fmt.Printf("%s\t%d\t%d\t%s\t%s\n", remote.IP, n, sport, st, server)
145 |
146 | select {
147 | case waiting[remote.IP.String()].c <- true:
148 | default:
149 | }
150 |
151 | delete(waiting, remote.IP.String())
152 |
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/mkindex.go:
--------------------------------------------------------------------------------
1 | // +build ignore
2 |
3 | // Copyright 2013 The Go Authors. All rights reserved.
4 | // Use of this source code is governed by a BSD-style
5 | // license that can be found in the LICENSE file.
6 |
7 | // Command mkindex creates the file "pkgindex.go" containing an index of the Go
8 | // standard library. The file is intended to be built as part of the imports
9 | // package, so that the package may be used in environments where a GOROOT is
10 | // not available (such as App Engine).
11 | package main
12 |
13 | import (
14 | "bytes"
15 | "fmt"
16 | "go/ast"
17 | "go/build"
18 | "go/format"
19 | "go/parser"
20 | "go/token"
21 | "io/ioutil"
22 | "log"
23 | "os"
24 | "path"
25 | "path/filepath"
26 | "strings"
27 | )
28 |
29 | var (
30 | pkgIndex = make(map[string][]pkg)
31 | exports = make(map[string]map[string]bool)
32 | )
33 |
34 | func main() {
35 | // Don't use GOPATH.
36 | ctx := build.Default
37 | ctx.GOPATH = ""
38 |
39 | // Populate pkgIndex global from GOROOT.
40 | for _, path := range ctx.SrcDirs() {
41 | f, err := os.Open(path)
42 | if err != nil {
43 | log.Print(err)
44 | continue
45 | }
46 | children, err := f.Readdir(-1)
47 | f.Close()
48 | if err != nil {
49 | log.Print(err)
50 | continue
51 | }
52 | for _, child := range children {
53 | if child.IsDir() {
54 | loadPkg(path, child.Name())
55 | }
56 | }
57 | }
58 | // Populate exports global.
59 | for _, ps := range pkgIndex {
60 | for _, p := range ps {
61 | e := loadExports(p.dir)
62 | if e != nil {
63 | exports[p.dir] = e
64 | }
65 | }
66 | }
67 |
68 | // Construct source file.
69 | var buf bytes.Buffer
70 | fmt.Fprint(&buf, pkgIndexHead)
71 | fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex)
72 | fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports)
73 | src := buf.Bytes()
74 |
75 | // Replace main.pkg type name with pkg.
76 | src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1)
77 | // Replace actual GOROOT with "/go".
78 | src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1)
79 | // Add some line wrapping.
80 | src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1)
81 | src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1)
82 |
83 | var err error
84 | src, err = format.Source(src)
85 | if err != nil {
86 | log.Fatal(err)
87 | }
88 |
89 | // Write out source file.
90 | err = ioutil.WriteFile("pkgindex.go", src, 0644)
91 | if err != nil {
92 | log.Fatal(err)
93 | }
94 | }
95 |
96 | const pkgIndexHead = `package imports
97 |
98 | func init() {
99 | pkgIndexOnce.Do(func() {
100 | pkgIndex.m = pkgIndexMaster
101 | })
102 | loadExports = func(dir string) map[string]bool {
103 | return exportsMaster[dir]
104 | }
105 | }
106 | `
107 |
108 | type pkg struct {
109 | importpath string // full pkg import path, e.g. "net/http"
110 | dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
111 | }
112 |
113 | var fset = token.NewFileSet()
114 |
115 | func loadPkg(root, importpath string) {
116 | shortName := path.Base(importpath)
117 | if shortName == "testdata" {
118 | return
119 | }
120 |
121 | dir := filepath.Join(root, importpath)
122 | pkgIndex[shortName] = append(pkgIndex[shortName], pkg{
123 | importpath: importpath,
124 | dir: dir,
125 | })
126 |
127 | pkgDir, err := os.Open(dir)
128 | if err != nil {
129 | return
130 | }
131 | children, err := pkgDir.Readdir(-1)
132 | pkgDir.Close()
133 | if err != nil {
134 | return
135 | }
136 | for _, child := range children {
137 | name := child.Name()
138 | if name == "" {
139 | continue
140 | }
141 | if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
142 | continue
143 | }
144 | if child.IsDir() {
145 | loadPkg(root, filepath.Join(importpath, name))
146 | }
147 | }
148 | }
149 |
150 | func loadExports(dir string) map[string]bool {
151 | exports := make(map[string]bool)
152 | buildPkg, err := build.ImportDir(dir, 0)
153 | if err != nil {
154 | if strings.Contains(err.Error(), "no buildable Go source files in") {
155 | return nil
156 | }
157 | log.Printf("could not import %q: %v", dir, err)
158 | return nil
159 | }
160 | for _, file := range buildPkg.GoFiles {
161 | f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
162 | if err != nil {
163 | log.Printf("could not parse %q: %v", file, err)
164 | continue
165 | }
166 | for name := range f.Scope.Objects {
167 | if ast.IsExported(name) {
168 | exports[name] = true
169 | }
170 | }
171 | }
172 | return exports
173 | }
174 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/fastwalk.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // A faster implementation of filepath.Walk.
6 | //
7 | // filepath.Walk's design necessarily calls os.Lstat on each file,
8 | // even if the caller needs less info. And goimports only need to know
9 | // the type of each file. The kernel interface provides the type in
10 | // the Readdir call but the standard library ignored it.
11 | // fastwalk_unix.go contains a fork of the syscall routines.
12 | //
13 | // See golang.org/issue/16399
14 |
15 | package imports
16 |
17 | import (
18 | "errors"
19 | "os"
20 | "path/filepath"
21 | "runtime"
22 | "sync"
23 | )
24 |
25 | // traverseLink is a sentinel error for fastWalk, similar to filepath.SkipDir.
26 | var traverseLink = errors.New("traverse symlink, assuming target is a directory")
27 |
28 | // fastWalk walks the file tree rooted at root, calling walkFn for
29 | // each file or directory in the tree, including root.
30 | //
31 | // If fastWalk returns filepath.SkipDir, the directory is skipped.
32 | //
33 | // Unlike filepath.Walk:
34 | // * file stat calls must be done by the user.
35 | // The only provided metadata is the file type, which does not include
36 | // any permission bits.
37 | // * multiple goroutines stat the filesystem concurrently. The provided
38 | // walkFn must be safe for concurrent use.
39 | // * fastWalk can follow symlinks if walkFn returns the traverseLink
40 | // sentinel error. It is the walkFn's responsibility to prevent
41 | // fastWalk from going into symlink cycles.
42 | func fastWalk(root string, walkFn func(path string, typ os.FileMode) error) error {
43 | // TODO(bradfitz): make numWorkers configurable? We used a
44 | // minimum of 4 to give the kernel more info about multiple
45 | // things we want, in hopes its I/O scheduling can take
46 | // advantage of that. Hopefully most are in cache. Maybe 4 is
47 | // even too low of a minimum. Profile more.
48 | numWorkers := 4
49 | if n := runtime.NumCPU(); n > numWorkers {
50 | numWorkers = n
51 | }
52 |
53 | // Make sure to wait for all workers to finish, otherwise
54 | // walkFn could still be called after returning. This Wait call
55 | // runs after close(e.donec) below.
56 | var wg sync.WaitGroup
57 | defer wg.Wait()
58 |
59 | w := &walker{
60 | fn: walkFn,
61 | enqueuec: make(chan walkItem, numWorkers), // buffered for performance
62 | workc: make(chan walkItem, numWorkers), // buffered for performance
63 | donec: make(chan struct{}),
64 |
65 | // buffered for correctness & not leaking goroutines:
66 | resc: make(chan error, numWorkers),
67 | }
68 | defer close(w.donec)
69 |
70 | for i := 0; i < numWorkers; i++ {
71 | wg.Add(1)
72 | go w.doWork(&wg)
73 | }
74 | todo := []walkItem{{dir: root}}
75 | out := 0
76 | for {
77 | workc := w.workc
78 | var workItem walkItem
79 | if len(todo) == 0 {
80 | workc = nil
81 | } else {
82 | workItem = todo[len(todo)-1]
83 | }
84 | select {
85 | case workc <- workItem:
86 | todo = todo[:len(todo)-1]
87 | out++
88 | case it := <-w.enqueuec:
89 | todo = append(todo, it)
90 | case err := <-w.resc:
91 | out--
92 | if err != nil {
93 | return err
94 | }
95 | if out == 0 && len(todo) == 0 {
96 | // It's safe to quit here, as long as the buffered
97 | // enqueue channel isn't also readable, which might
98 | // happen if the worker sends both another unit of
99 | // work and its result before the other select was
100 | // scheduled and both w.resc and w.enqueuec were
101 | // readable.
102 | select {
103 | case it := <-w.enqueuec:
104 | todo = append(todo, it)
105 | default:
106 | return nil
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
113 | // doWork reads directories as instructed (via workc) and runs the
114 | // user's callback function.
115 | func (w *walker) doWork(wg *sync.WaitGroup) {
116 | defer wg.Done()
117 | for {
118 | select {
119 | case <-w.donec:
120 | return
121 | case it := <-w.workc:
122 | select {
123 | case <-w.donec:
124 | return
125 | case w.resc <- w.walk(it.dir, !it.callbackDone):
126 | }
127 | }
128 | }
129 | }
130 |
131 | type walker struct {
132 | fn func(path string, typ os.FileMode) error
133 |
134 | donec chan struct{} // closed on fastWalk's return
135 | workc chan walkItem // to workers
136 | enqueuec chan walkItem // from workers
137 | resc chan error // from workers
138 | }
139 |
140 | type walkItem struct {
141 | dir string
142 | callbackDone bool // callback already called; don't do it again
143 | }
144 |
145 | func (w *walker) enqueue(it walkItem) {
146 | select {
147 | case w.enqueuec <- it:
148 | case <-w.donec:
149 | }
150 | }
151 |
152 | func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error {
153 | joined := dirName + string(os.PathSeparator) + baseName
154 | if typ == os.ModeDir {
155 | w.enqueue(walkItem{dir: joined})
156 | return nil
157 | }
158 |
159 | err := w.fn(joined, typ)
160 | if typ == os.ModeSymlink {
161 | if err == traverseLink {
162 | // Set callbackDone so we don't call it twice for both the
163 | // symlink-as-symlink and the symlink-as-directory later:
164 | w.enqueue(walkItem{dir: joined, callbackDone: true})
165 | return nil
166 | }
167 | if err == filepath.SkipDir {
168 | // Permit SkipDir on symlinks too.
169 | return nil
170 | }
171 | }
172 | return err
173 | }
174 |
175 | func (w *walker) walk(root string, runUserCallback bool) error {
176 | if runUserCallback {
177 | err := w.fn(root, os.ModeDir)
178 | if err == filepath.SkipDir {
179 | return nil
180 | }
181 | if err != nil {
182 | return err
183 | }
184 | }
185 |
186 | return readDir(root, w.onDirEnt)
187 | }
188 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # The import path is where your repository can be found.
2 | # To import subpackages, always prepend the full import path.
3 | # If you change this, run `make clean`. Read more: https://git.io/vM7zV
4 | IMPORT_PATH := github.com/benjojo/badupnp
5 |
6 | # V := 1 # When V is set, print commands and build progress.
7 |
8 | # Space separated patterns of packages to skip in list, test, format.
9 | IGNORED_PACKAGES := /vendor/
10 |
11 | .PHONY: all
12 | all: build
13 |
14 | .PHONY: build
15 | build: .GOPATH/.ok
16 | $Q go install $(if $V,-v) $(VERSION_FLAGS) $(IMPORT_PATH)
17 |
18 | ### Code not in the repository root? Another binary? Add to the path like this.
19 | # .PHONY: otherbin
20 | # otherbin: .GOPATH/.ok
21 | # $Q go install $(if $V,-v) $(VERSION_FLAGS) $(IMPORT_PATH)/cmd/otherbin
22 |
23 | ##### ^^^^^^ EDIT ABOVE ^^^^^^ #####
24 |
25 | ##### =====> Utility targets <===== #####
26 |
27 | .PHONY: clean test list cover format
28 |
29 | clean:
30 | $Q rm -rf bin .GOPATH
31 |
32 | test: .GOPATH/.ok
33 | $Q go test $(if $V,-v) -i -race $(allpackages) # install -race libs to speed up next run
34 | ifndef CI
35 | $Q go vet $(allpackages)
36 | $Q GODEBUG=cgocheck=2 go test -race $(allpackages)
37 | else
38 | $Q ( go vet $(allpackages); echo $$? ) | \
39 | tee .GOPATH/test/vet.txt | sed '$$ d'; exit $$(tail -1 .GOPATH/test/vet.txt)
40 | $Q ( GODEBUG=cgocheck=2 go test -v -race $(allpackages); echo $$? ) | \
41 | tee .GOPATH/test/output.txt | sed '$$ d'; exit $$(tail -1 .GOPATH/test/output.txt)
42 | endif
43 |
44 | list: .GOPATH/.ok
45 | @echo $(allpackages)
46 |
47 | cover: bin/gocovmerge .GOPATH/.ok
48 | @echo "NOTE: make cover does not exit 1 on failure, don't use it to check for tests success!"
49 | $Q rm -f .GOPATH/cover/*.out .GOPATH/cover/all.merged
50 | $(if $V,@echo "-- go test -coverpkg=./... -coverprofile=.GOPATH/cover/... ./...")
51 | @for MOD in $(allpackages); do \
52 | go test -coverpkg=`echo $(allpackages)|tr " " ","` \
53 | -coverprofile=.GOPATH/cover/unit-`echo $$MOD|tr "/" "_"`.out \
54 | $$MOD 2>&1 | grep -v "no packages being tested depend on"; \
55 | done
56 | $Q ./bin/gocovmerge .GOPATH/cover/*.out > .GOPATH/cover/all.merged
57 | ifndef CI
58 | $Q go tool cover -html .GOPATH/cover/all.merged
59 | else
60 | $Q go tool cover -html .GOPATH/cover/all.merged -o .GOPATH/cover/all.html
61 | endif
62 | @echo ""
63 | @echo "=====> Total test coverage: <====="
64 | @echo ""
65 | $Q go tool cover -func .GOPATH/cover/all.merged
66 |
67 | format: bin/goimports .GOPATH/.ok
68 | $Q find .GOPATH/src/$(IMPORT_PATH)/ -iname \*.go | grep -v \
69 | -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES)) | xargs ./bin/goimports -w
70 |
71 | ##### =====> Internals <===== #####
72 |
73 | .PHONY: setup
74 | setup: clean .GOPATH/.ok
75 | @if ! grep "/.GOPATH" .gitignore > /dev/null 2>&1; then \
76 | echo "/.GOPATH" >> .gitignore; \
77 | echo "/bin" >> .gitignore; \
78 | fi
79 | go get -u github.com/FiloSottile/gvt
80 | - ./bin/gvt fetch golang.org/x/tools/cmd/goimports
81 | - ./bin/gvt fetch github.com/wadey/gocovmerge
82 |
83 | VERSION := $(shell git describe --tags --always --dirty="-dev")
84 | DATE := $(shell date -u '+%Y-%m-%d-%H%M UTC')
85 | VERSION_FLAGS := -ldflags='-X "main.Version=$(VERSION)" -X "main.BuildTime=$(DATE)"'
86 |
87 | # cd into the GOPATH to workaround ./... not following symlinks
88 | _allpackages = $(shell ( cd $(CURDIR)/.GOPATH/src/$(IMPORT_PATH) && \
89 | GOPATH=$(CURDIR)/.GOPATH go list ./... 2>&1 1>&3 | \
90 | grep -v -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES)) 1>&2 ) 3>&1 | \
91 | grep -v -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES)))
92 |
93 | # memoize allpackages, so that it's executed only once and only if used
94 | allpackages = $(if $(__allpackages),,$(eval __allpackages := $$(_allpackages)))$(__allpackages)
95 |
96 | export GOPATH := $(CURDIR)/.GOPATH
97 | unexport GOBIN
98 |
99 | Q := $(if $V,,@)
100 |
101 | .GOPATH/.ok:
102 | $Q mkdir -p "$(dir .GOPATH/src/$(IMPORT_PATH))"
103 | $Q ln -s ../../../.. ".GOPATH/src/$(IMPORT_PATH)"
104 | $Q mkdir -p .GOPATH/test .GOPATH/cover
105 | $Q mkdir -p bin
106 | $Q ln -s ../bin .GOPATH/bin
107 | $Q touch $@
108 |
109 | .PHONY: bin/gocovmerge bin/goimports
110 | bin/gocovmerge: .GOPATH/.ok
111 | @test -d ./vendor/github.com/wadey/gocovmerge || \
112 | { echo "Vendored gocovmerge not found, try running 'make setup'..."; exit 1; }
113 | $Q go install $(IMPORT_PATH)/vendor/github.com/wadey/gocovmerge
114 | bin/goimports: .GOPATH/.ok
115 | @test -d ./vendor/golang.org/x/tools/cmd/goimports || \
116 | { echo "Vendored goimports not found, try running 'make setup'..."; exit 1; }
117 | $Q go install $(IMPORT_PATH)/vendor/golang.org/x/tools/cmd/goimports
118 |
119 | # Based on https://github.com/cloudflare/hellogopher - v1.1 - MIT License
120 | #
121 | # Copyright (c) 2017 Cloudflare
122 | #
123 | # Permission is hereby granted, free of charge, to any person obtaining a copy
124 | # of this software and associated documentation files (the "Software"), to deal
125 | # in the Software without restriction, including without limitation the rights
126 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
127 | # copies of the Software, and to permit persons to whom the Software is
128 | # furnished to do so, subject to the following conditions:
129 | #
130 | # The above copyright notice and this permission notice shall be included in all
131 | # copies or substantial portions of the Software.
132 | #
133 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
134 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
135 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
136 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
137 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
138 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
139 | # SOFTWARE.
140 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/cover/profile.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package cover provides support for parsing coverage profiles
6 | // generated by "go test -coverprofile=cover.out".
7 | package cover // import "golang.org/x/tools/cover"
8 |
9 | import (
10 | "bufio"
11 | "fmt"
12 | "math"
13 | "os"
14 | "regexp"
15 | "sort"
16 | "strconv"
17 | "strings"
18 | )
19 |
20 | // Profile represents the profiling data for a specific file.
21 | type Profile struct {
22 | FileName string
23 | Mode string
24 | Blocks []ProfileBlock
25 | }
26 |
27 | // ProfileBlock represents a single block of profiling data.
28 | type ProfileBlock struct {
29 | StartLine, StartCol int
30 | EndLine, EndCol int
31 | NumStmt, Count int
32 | }
33 |
34 | type byFileName []*Profile
35 |
36 | func (p byFileName) Len() int { return len(p) }
37 | func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName }
38 | func (p byFileName) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
39 |
40 | // ParseProfiles parses profile data in the specified file and returns a
41 | // Profile for each source file described therein.
42 | func ParseProfiles(fileName string) ([]*Profile, error) {
43 | pf, err := os.Open(fileName)
44 | if err != nil {
45 | return nil, err
46 | }
47 | defer pf.Close()
48 |
49 | files := make(map[string]*Profile)
50 | buf := bufio.NewReader(pf)
51 | // First line is "mode: foo", where foo is "set", "count", or "atomic".
52 | // Rest of file is in the format
53 | // encoding/base64/base64.go:34.44,37.40 3 1
54 | // where the fields are: name.go:line.column,line.column numberOfStatements count
55 | s := bufio.NewScanner(buf)
56 | mode := ""
57 | for s.Scan() {
58 | line := s.Text()
59 | if mode == "" {
60 | const p = "mode: "
61 | if !strings.HasPrefix(line, p) || line == p {
62 | return nil, fmt.Errorf("bad mode line: %v", line)
63 | }
64 | mode = line[len(p):]
65 | continue
66 | }
67 | m := lineRe.FindStringSubmatch(line)
68 | if m == nil {
69 | return nil, fmt.Errorf("line %q doesn't match expected format: %v", line, lineRe)
70 | }
71 | fn := m[1]
72 | p := files[fn]
73 | if p == nil {
74 | p = &Profile{
75 | FileName: fn,
76 | Mode: mode,
77 | }
78 | files[fn] = p
79 | }
80 | p.Blocks = append(p.Blocks, ProfileBlock{
81 | StartLine: toInt(m[2]),
82 | StartCol: toInt(m[3]),
83 | EndLine: toInt(m[4]),
84 | EndCol: toInt(m[5]),
85 | NumStmt: toInt(m[6]),
86 | Count: toInt(m[7]),
87 | })
88 | }
89 | if err := s.Err(); err != nil {
90 | return nil, err
91 | }
92 | for _, p := range files {
93 | sort.Sort(blocksByStart(p.Blocks))
94 | }
95 | // Generate a sorted slice.
96 | profiles := make([]*Profile, 0, len(files))
97 | for _, profile := range files {
98 | profiles = append(profiles, profile)
99 | }
100 | sort.Sort(byFileName(profiles))
101 | return profiles, nil
102 | }
103 |
104 | type blocksByStart []ProfileBlock
105 |
106 | func (b blocksByStart) Len() int { return len(b) }
107 | func (b blocksByStart) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
108 | func (b blocksByStart) Less(i, j int) bool {
109 | bi, bj := b[i], b[j]
110 | return bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol
111 | }
112 |
113 | var lineRe = regexp.MustCompile(`^(.+):([0-9]+).([0-9]+),([0-9]+).([0-9]+) ([0-9]+) ([0-9]+)$`)
114 |
115 | func toInt(s string) int {
116 | i, err := strconv.Atoi(s)
117 | if err != nil {
118 | panic(err)
119 | }
120 | return i
121 | }
122 |
123 | // Boundary represents the position in a source file of the beginning or end of a
124 | // block as reported by the coverage profile. In HTML mode, it will correspond to
125 | // the opening or closing of a tag and will be used to colorize the source
126 | type Boundary struct {
127 | Offset int // Location as a byte offset in the source file.
128 | Start bool // Is this the start of a block?
129 | Count int // Event count from the cover profile.
130 | Norm float64 // Count normalized to [0..1].
131 | }
132 |
133 | // Boundaries returns a Profile as a set of Boundary objects within the provided src.
134 | func (p *Profile) Boundaries(src []byte) (boundaries []Boundary) {
135 | // Find maximum count.
136 | max := 0
137 | for _, b := range p.Blocks {
138 | if b.Count > max {
139 | max = b.Count
140 | }
141 | }
142 | // Divisor for normalization.
143 | divisor := math.Log(float64(max))
144 |
145 | // boundary returns a Boundary, populating the Norm field with a normalized Count.
146 | boundary := func(offset int, start bool, count int) Boundary {
147 | b := Boundary{Offset: offset, Start: start, Count: count}
148 | if !start || count == 0 {
149 | return b
150 | }
151 | if max <= 1 {
152 | b.Norm = 0.8 // Profile is in"set" mode; we want a heat map. Use cov8 in the CSS.
153 | } else if count > 0 {
154 | b.Norm = math.Log(float64(count)) / divisor
155 | }
156 | return b
157 | }
158 |
159 | line, col := 1, 2 // TODO: Why is this 2?
160 | for si, bi := 0, 0; si < len(src) && bi < len(p.Blocks); {
161 | b := p.Blocks[bi]
162 | if b.StartLine == line && b.StartCol == col {
163 | boundaries = append(boundaries, boundary(si, true, b.Count))
164 | }
165 | if b.EndLine == line && b.EndCol == col || line > b.EndLine {
166 | boundaries = append(boundaries, boundary(si, false, 0))
167 | bi++
168 | continue // Don't advance through src; maybe the next block starts here.
169 | }
170 | if src[si] == '\n' {
171 | line++
172 | col = 0
173 | }
174 | col++
175 | si++
176 | }
177 | sort.Sort(boundariesByPos(boundaries))
178 | return
179 | }
180 |
181 | type boundariesByPos []Boundary
182 |
183 | func (b boundariesByPos) Len() int { return len(b) }
184 | func (b boundariesByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
185 | func (b boundariesByPos) Less(i, j int) bool {
186 | if b[i].Offset == b[j].Offset {
187 | return !b[i].Start && b[j].Start
188 | }
189 | return b[i].Offset < b[j].Offset
190 | }
191 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/sortimports.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Hacked up copy of go/ast/import.go
6 |
7 | package imports
8 |
9 | import (
10 | "go/ast"
11 | "go/token"
12 | "sort"
13 | "strconv"
14 | )
15 |
16 | // sortImports sorts runs of consecutive import lines in import blocks in f.
17 | // It also removes duplicate imports when it is possible to do so without data loss.
18 | func sortImports(fset *token.FileSet, f *ast.File) {
19 | for i, d := range f.Decls {
20 | d, ok := d.(*ast.GenDecl)
21 | if !ok || d.Tok != token.IMPORT {
22 | // Not an import declaration, so we're done.
23 | // Imports are always first.
24 | break
25 | }
26 |
27 | if len(d.Specs) == 0 {
28 | // Empty import block, remove it.
29 | f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
30 | }
31 |
32 | if !d.Lparen.IsValid() {
33 | // Not a block: sorted by default.
34 | continue
35 | }
36 |
37 | // Identify and sort runs of specs on successive lines.
38 | i := 0
39 | specs := d.Specs[:0]
40 | for j, s := range d.Specs {
41 | if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
42 | // j begins a new run. End this one.
43 | specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
44 | i = j
45 | }
46 | }
47 | specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
48 | d.Specs = specs
49 |
50 | // Deduping can leave a blank line before the rparen; clean that up.
51 | if len(d.Specs) > 0 {
52 | lastSpec := d.Specs[len(d.Specs)-1]
53 | lastLine := fset.Position(lastSpec.Pos()).Line
54 | if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
55 | fset.File(d.Rparen).MergeLine(rParenLine - 1)
56 | }
57 | }
58 | }
59 | }
60 |
61 | func importPath(s ast.Spec) string {
62 | t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value)
63 | if err == nil {
64 | return t
65 | }
66 | return ""
67 | }
68 |
69 | func importName(s ast.Spec) string {
70 | n := s.(*ast.ImportSpec).Name
71 | if n == nil {
72 | return ""
73 | }
74 | return n.Name
75 | }
76 |
77 | func importComment(s ast.Spec) string {
78 | c := s.(*ast.ImportSpec).Comment
79 | if c == nil {
80 | return ""
81 | }
82 | return c.Text()
83 | }
84 |
85 | // collapse indicates whether prev may be removed, leaving only next.
86 | func collapse(prev, next ast.Spec) bool {
87 | if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
88 | return false
89 | }
90 | return prev.(*ast.ImportSpec).Comment == nil
91 | }
92 |
93 | type posSpan struct {
94 | Start token.Pos
95 | End token.Pos
96 | }
97 |
98 | func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
99 | // Can't short-circuit here even if specs are already sorted,
100 | // since they might yet need deduplication.
101 | // A lone import, however, may be safely ignored.
102 | if len(specs) <= 1 {
103 | return specs
104 | }
105 |
106 | // Record positions for specs.
107 | pos := make([]posSpan, len(specs))
108 | for i, s := range specs {
109 | pos[i] = posSpan{s.Pos(), s.End()}
110 | }
111 |
112 | // Identify comments in this range.
113 | // Any comment from pos[0].Start to the final line counts.
114 | lastLine := fset.Position(pos[len(pos)-1].End).Line
115 | cstart := len(f.Comments)
116 | cend := len(f.Comments)
117 | for i, g := range f.Comments {
118 | if g.Pos() < pos[0].Start {
119 | continue
120 | }
121 | if i < cstart {
122 | cstart = i
123 | }
124 | if fset.Position(g.End()).Line > lastLine {
125 | cend = i
126 | break
127 | }
128 | }
129 | comments := f.Comments[cstart:cend]
130 |
131 | // Assign each comment to the import spec preceding it.
132 | importComment := map[*ast.ImportSpec][]*ast.CommentGroup{}
133 | specIndex := 0
134 | for _, g := range comments {
135 | for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
136 | specIndex++
137 | }
138 | s := specs[specIndex].(*ast.ImportSpec)
139 | importComment[s] = append(importComment[s], g)
140 | }
141 |
142 | // Sort the import specs by import path.
143 | // Remove duplicates, when possible without data loss.
144 | // Reassign the import paths to have the same position sequence.
145 | // Reassign each comment to abut the end of its spec.
146 | // Sort the comments by new position.
147 | sort.Sort(byImportSpec(specs))
148 |
149 | // Dedup. Thanks to our sorting, we can just consider
150 | // adjacent pairs of imports.
151 | deduped := specs[:0]
152 | for i, s := range specs {
153 | if i == len(specs)-1 || !collapse(s, specs[i+1]) {
154 | deduped = append(deduped, s)
155 | } else {
156 | p := s.Pos()
157 | fset.File(p).MergeLine(fset.Position(p).Line)
158 | }
159 | }
160 | specs = deduped
161 |
162 | // Fix up comment positions
163 | for i, s := range specs {
164 | s := s.(*ast.ImportSpec)
165 | if s.Name != nil {
166 | s.Name.NamePos = pos[i].Start
167 | }
168 | s.Path.ValuePos = pos[i].Start
169 | s.EndPos = pos[i].End
170 | for _, g := range importComment[s] {
171 | for _, c := range g.List {
172 | c.Slash = pos[i].End
173 | }
174 | }
175 | }
176 |
177 | sort.Sort(byCommentPos(comments))
178 |
179 | return specs
180 | }
181 |
182 | type byImportSpec []ast.Spec // slice of *ast.ImportSpec
183 |
184 | func (x byImportSpec) Len() int { return len(x) }
185 | func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
186 | func (x byImportSpec) Less(i, j int) bool {
187 | ipath := importPath(x[i])
188 | jpath := importPath(x[j])
189 |
190 | igroup := importGroup(ipath)
191 | jgroup := importGroup(jpath)
192 | if igroup != jgroup {
193 | return igroup < jgroup
194 | }
195 |
196 | if ipath != jpath {
197 | return ipath < jpath
198 | }
199 | iname := importName(x[i])
200 | jname := importName(x[j])
201 |
202 | if iname != jname {
203 | return iname < jname
204 | }
205 | return importComment(x[i]) < importComment(x[j])
206 | }
207 |
208 | type byCommentPos []*ast.CommentGroup
209 |
210 | func (x byCommentPos) Len() int { return len(x) }
211 | func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
212 | func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
213 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/imports.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:generate go run mkstdlib.go
6 |
7 | // Package imports implements a Go pretty-printer (like package "go/format")
8 | // that also adds or removes import statements as necessary.
9 | package imports // import "golang.org/x/tools/imports"
10 |
11 | import (
12 | "bufio"
13 | "bytes"
14 | "fmt"
15 | "go/ast"
16 | "go/format"
17 | "go/parser"
18 | "go/printer"
19 | "go/token"
20 | "io"
21 | "regexp"
22 | "strconv"
23 | "strings"
24 |
25 | "golang.org/x/tools/go/ast/astutil"
26 | )
27 |
28 | // Options specifies options for processing files.
29 | type Options struct {
30 | Fragment bool // Accept fragment of a source file (no package statement)
31 | AllErrors bool // Report all errors (not just the first 10 on different lines)
32 |
33 | Comments bool // Print comments (true if nil *Options provided)
34 | TabIndent bool // Use tabs for indent (true if nil *Options provided)
35 | TabWidth int // Tab width (8 if nil *Options provided)
36 |
37 | FormatOnly bool // Disable the insertion and deletion of imports
38 | }
39 |
40 | // Process formats and adjusts imports for the provided file.
41 | // If opt is nil the defaults are used.
42 | //
43 | // Note that filename's directory influences which imports can be chosen,
44 | // so it is important that filename be accurate.
45 | // To process data ``as if'' it were in filename, pass the data as a non-nil src.
46 | func Process(filename string, src []byte, opt *Options) ([]byte, error) {
47 | if opt == nil {
48 | opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
49 | }
50 |
51 | fileSet := token.NewFileSet()
52 | file, adjust, err := parse(fileSet, filename, src, opt)
53 | if err != nil {
54 | return nil, err
55 | }
56 |
57 | if !opt.FormatOnly {
58 | _, err = fixImports(fileSet, file, filename)
59 | if err != nil {
60 | return nil, err
61 | }
62 | }
63 |
64 | sortImports(fileSet, file)
65 | imps := astutil.Imports(fileSet, file)
66 |
67 | var spacesBefore []string // import paths we need spaces before
68 | for _, impSection := range imps {
69 | // Within each block of contiguous imports, see if any
70 | // import lines are in different group numbers. If so,
71 | // we'll need to put a space between them so it's
72 | // compatible with gofmt.
73 | lastGroup := -1
74 | for _, importSpec := range impSection {
75 | importPath, _ := strconv.Unquote(importSpec.Path.Value)
76 | groupNum := importGroup(importPath)
77 | if groupNum != lastGroup && lastGroup != -1 {
78 | spacesBefore = append(spacesBefore, importPath)
79 | }
80 | lastGroup = groupNum
81 | }
82 |
83 | }
84 |
85 | printerMode := printer.UseSpaces
86 | if opt.TabIndent {
87 | printerMode |= printer.TabIndent
88 | }
89 | printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth}
90 |
91 | var buf bytes.Buffer
92 | err = printConfig.Fprint(&buf, fileSet, file)
93 | if err != nil {
94 | return nil, err
95 | }
96 | out := buf.Bytes()
97 | if adjust != nil {
98 | out = adjust(src, out)
99 | }
100 | if len(spacesBefore) > 0 {
101 | out = addImportSpaces(bytes.NewReader(out), spacesBefore)
102 | }
103 |
104 | out, err = format.Source(out)
105 | if err != nil {
106 | return nil, err
107 | }
108 | return out, nil
109 | }
110 |
111 | // parse parses src, which was read from filename,
112 | // as a Go source file or statement list.
113 | func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) {
114 | parserMode := parser.Mode(0)
115 | if opt.Comments {
116 | parserMode |= parser.ParseComments
117 | }
118 | if opt.AllErrors {
119 | parserMode |= parser.AllErrors
120 | }
121 |
122 | // Try as whole source file.
123 | file, err := parser.ParseFile(fset, filename, src, parserMode)
124 | if err == nil {
125 | return file, nil, nil
126 | }
127 | // If the error is that the source file didn't begin with a
128 | // package line and we accept fragmented input, fall through to
129 | // try as a source fragment. Stop and return on any other error.
130 | if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") {
131 | return nil, nil, err
132 | }
133 |
134 | // If this is a declaration list, make it a source file
135 | // by inserting a package clause.
136 | // Insert using a ;, not a newline, so that the line numbers
137 | // in psrc match the ones in src.
138 | psrc := append([]byte("package main;"), src...)
139 | file, err = parser.ParseFile(fset, filename, psrc, parserMode)
140 | if err == nil {
141 | // If a main function exists, we will assume this is a main
142 | // package and leave the file.
143 | if containsMainFunc(file) {
144 | return file, nil, nil
145 | }
146 |
147 | adjust := func(orig, src []byte) []byte {
148 | // Remove the package clause.
149 | // Gofmt has turned the ; into a \n.
150 | src = src[len("package main\n"):]
151 | return matchSpace(orig, src)
152 | }
153 | return file, adjust, nil
154 | }
155 | // If the error is that the source file didn't begin with a
156 | // declaration, fall through to try as a statement list.
157 | // Stop and return on any other error.
158 | if !strings.Contains(err.Error(), "expected declaration") {
159 | return nil, nil, err
160 | }
161 |
162 | // If this is a statement list, make it a source file
163 | // by inserting a package clause and turning the list
164 | // into a function body. This handles expressions too.
165 | // Insert using a ;, not a newline, so that the line numbers
166 | // in fsrc match the ones in src.
167 | fsrc := append(append([]byte("package p; func _() {"), src...), '}')
168 | file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
169 | if err == nil {
170 | adjust := func(orig, src []byte) []byte {
171 | // Remove the wrapping.
172 | // Gofmt has turned the ; into a \n\n.
173 | src = src[len("package p\n\nfunc _() {"):]
174 | src = src[:len(src)-len("}\n")]
175 | // Gofmt has also indented the function body one level.
176 | // Remove that indent.
177 | src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
178 | return matchSpace(orig, src)
179 | }
180 | return file, adjust, nil
181 | }
182 |
183 | // Failed, and out of options.
184 | return nil, nil, err
185 | }
186 |
187 | // containsMainFunc checks if a file contains a function declaration with the
188 | // function signature 'func main()'
189 | func containsMainFunc(file *ast.File) bool {
190 | for _, decl := range file.Decls {
191 | if f, ok := decl.(*ast.FuncDecl); ok {
192 | if f.Name.Name != "main" {
193 | continue
194 | }
195 |
196 | if len(f.Type.Params.List) != 0 {
197 | continue
198 | }
199 |
200 | if f.Type.Results != nil && len(f.Type.Results.List) != 0 {
201 | continue
202 | }
203 |
204 | return true
205 | }
206 | }
207 |
208 | return false
209 | }
210 |
211 | func cutSpace(b []byte) (before, middle, after []byte) {
212 | i := 0
213 | for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
214 | i++
215 | }
216 | j := len(b)
217 | for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
218 | j--
219 | }
220 | if i <= j {
221 | return b[:i], b[i:j], b[j:]
222 | }
223 | return nil, nil, b[j:]
224 | }
225 |
226 | // matchSpace reformats src to use the same space context as orig.
227 | // 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
228 | // 2) matchSpace copies the indentation of the first non-blank line in orig
229 | // to every non-blank line in src.
230 | // 3) matchSpace copies the trailing space from orig and uses it in place
231 | // of src's trailing space.
232 | func matchSpace(orig []byte, src []byte) []byte {
233 | before, _, after := cutSpace(orig)
234 | i := bytes.LastIndex(before, []byte{'\n'})
235 | before, indent := before[:i+1], before[i+1:]
236 |
237 | _, src, _ = cutSpace(src)
238 |
239 | var b bytes.Buffer
240 | b.Write(before)
241 | for len(src) > 0 {
242 | line := src
243 | if i := bytes.IndexByte(line, '\n'); i >= 0 {
244 | line, src = line[:i+1], line[i+1:]
245 | } else {
246 | src = nil
247 | }
248 | if len(line) > 0 && line[0] != '\n' { // not blank
249 | b.Write(indent)
250 | }
251 | b.Write(line)
252 | }
253 | b.Write(after)
254 | return b.Bytes()
255 | }
256 |
257 | var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`)
258 |
259 | func addImportSpaces(r io.Reader, breaks []string) []byte {
260 | var out bytes.Buffer
261 | sc := bufio.NewScanner(r)
262 | inImports := false
263 | done := false
264 | for sc.Scan() {
265 | s := sc.Text()
266 |
267 | if !inImports && !done && strings.HasPrefix(s, "import") {
268 | inImports = true
269 | }
270 | if inImports && (strings.HasPrefix(s, "var") ||
271 | strings.HasPrefix(s, "func") ||
272 | strings.HasPrefix(s, "const") ||
273 | strings.HasPrefix(s, "type")) {
274 | done = true
275 | inImports = false
276 | }
277 | if inImports && len(breaks) > 0 {
278 | if m := impLine.FindStringSubmatch(s); m != nil {
279 | if m[1] == breaks[0] {
280 | out.WriteByte('\n')
281 | breaks = breaks[1:]
282 | }
283 | }
284 | }
285 |
286 | fmt.Fprintln(&out, s)
287 | }
288 | return out.Bytes()
289 | }
290 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/cmd/goimports/goimports.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "bufio"
9 | "bytes"
10 | "errors"
11 | "flag"
12 | "fmt"
13 | "go/scanner"
14 | "io"
15 | "io/ioutil"
16 | "log"
17 | "os"
18 | "os/exec"
19 | "path/filepath"
20 | "runtime"
21 | "runtime/pprof"
22 | "strings"
23 |
24 | "golang.org/x/tools/imports"
25 | )
26 |
27 | var (
28 | // main operation modes
29 | list = flag.Bool("l", false, "list files whose formatting differs from goimport's")
30 | write = flag.Bool("w", false, "write result to (source) file instead of stdout")
31 | doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
32 | srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.")
33 | verbose bool // verbose logging
34 |
35 | cpuProfile = flag.String("cpuprofile", "", "CPU profile output")
36 | memProfile = flag.String("memprofile", "", "memory profile output")
37 | memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate")
38 |
39 | options = &imports.Options{
40 | TabWidth: 8,
41 | TabIndent: true,
42 | Comments: true,
43 | Fragment: true,
44 | }
45 | exitCode = 0
46 | )
47 |
48 | func init() {
49 | flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
50 | flag.StringVar(&imports.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages")
51 | }
52 |
53 | func report(err error) {
54 | scanner.PrintError(os.Stderr, err)
55 | exitCode = 2
56 | }
57 |
58 | func usage() {
59 | fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
60 | flag.PrintDefaults()
61 | os.Exit(2)
62 | }
63 |
64 | func isGoFile(f os.FileInfo) bool {
65 | // ignore non-Go files
66 | name := f.Name()
67 | return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
68 | }
69 |
70 | // argumentType is which mode goimports was invoked as.
71 | type argumentType int
72 |
73 | const (
74 | // fromStdin means the user is piping their source into goimports.
75 | fromStdin argumentType = iota
76 |
77 | // singleArg is the common case from editors, when goimports is run on
78 | // a single file.
79 | singleArg
80 |
81 | // multipleArg is when the user ran "goimports file1.go file2.go"
82 | // or ran goimports on a directory tree.
83 | multipleArg
84 | )
85 |
86 | func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error {
87 | opt := options
88 | if argType == fromStdin {
89 | nopt := *options
90 | nopt.Fragment = true
91 | opt = &nopt
92 | }
93 |
94 | if in == nil {
95 | f, err := os.Open(filename)
96 | if err != nil {
97 | return err
98 | }
99 | defer f.Close()
100 | in = f
101 | }
102 |
103 | src, err := ioutil.ReadAll(in)
104 | if err != nil {
105 | return err
106 | }
107 |
108 | target := filename
109 | if *srcdir != "" {
110 | // Determine whether the provided -srcdirc is a directory or file
111 | // and then use it to override the target.
112 | //
113 | // See https://github.com/dominikh/go-mode.el/issues/146
114 | if isFile(*srcdir) {
115 | if argType == multipleArg {
116 | return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories")
117 | }
118 | target = *srcdir
119 | } else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) {
120 | // For a file which doesn't exist on disk yet, but might shortly.
121 | // e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
122 | // The goimports on-save hook writes the buffer to a temp file
123 | // first and runs goimports before the actual save to newfile.go.
124 | // The editor's buffer is named "newfile.go" so that is passed to goimports as:
125 | // goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
126 | // and then the editor reloads the result from the tmp file and writes
127 | // it to newfile.go.
128 | target = *srcdir
129 | } else {
130 | // Pretend that file is from *srcdir in order to decide
131 | // visible imports correctly.
132 | target = filepath.Join(*srcdir, filepath.Base(filename))
133 | }
134 | }
135 |
136 | res, err := imports.Process(target, src, opt)
137 | if err != nil {
138 | return err
139 | }
140 |
141 | if !bytes.Equal(src, res) {
142 | // formatting has changed
143 | if *list {
144 | fmt.Fprintln(out, filename)
145 | }
146 | if *write {
147 | err = ioutil.WriteFile(filename, res, 0)
148 | if err != nil {
149 | return err
150 | }
151 | }
152 | if *doDiff {
153 | data, err := diff(src, res, filename)
154 | if err != nil {
155 | return fmt.Errorf("computing diff: %s", err)
156 | }
157 | fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
158 | out.Write(data)
159 | }
160 | }
161 |
162 | if !*list && !*write && !*doDiff {
163 | _, err = out.Write(res)
164 | }
165 |
166 | return err
167 | }
168 |
169 | func visitFile(path string, f os.FileInfo, err error) error {
170 | if err == nil && isGoFile(f) {
171 | err = processFile(path, nil, os.Stdout, multipleArg)
172 | }
173 | if err != nil {
174 | report(err)
175 | }
176 | return nil
177 | }
178 |
179 | func walkDir(path string) {
180 | filepath.Walk(path, visitFile)
181 | }
182 |
183 | func main() {
184 | runtime.GOMAXPROCS(runtime.NumCPU())
185 |
186 | // call gofmtMain in a separate function
187 | // so that it can use defer and have them
188 | // run before the exit.
189 | gofmtMain()
190 | os.Exit(exitCode)
191 | }
192 |
193 | // parseFlags parses command line flags and returns the paths to process.
194 | // It's a var so that custom implementations can replace it in other files.
195 | var parseFlags = func() []string {
196 | flag.BoolVar(&verbose, "v", false, "verbose logging")
197 |
198 | flag.Parse()
199 | return flag.Args()
200 | }
201 |
202 | func bufferedFileWriter(dest string) (w io.Writer, close func()) {
203 | f, err := os.Create(dest)
204 | if err != nil {
205 | log.Fatal(err)
206 | }
207 | bw := bufio.NewWriter(f)
208 | return bw, func() {
209 | if err := bw.Flush(); err != nil {
210 | log.Fatalf("error flushing %v: %v", dest, err)
211 | }
212 | if err := f.Close(); err != nil {
213 | log.Fatal(err)
214 | }
215 | }
216 | }
217 |
218 | func gofmtMain() {
219 | flag.Usage = usage
220 | paths := parseFlags()
221 |
222 | if *cpuProfile != "" {
223 | bw, flush := bufferedFileWriter(*cpuProfile)
224 | pprof.StartCPUProfile(bw)
225 | defer flush()
226 | defer pprof.StopCPUProfile()
227 | }
228 | // doTrace is a conditionally compiled wrapper around runtime/trace. It is
229 | // used to allow goimports to compile under gccgo, which does not support
230 | // runtime/trace. See https://golang.org/issue/15544.
231 | defer doTrace()()
232 | if *memProfileRate > 0 {
233 | runtime.MemProfileRate = *memProfileRate
234 | bw, flush := bufferedFileWriter(*memProfile)
235 | defer func() {
236 | runtime.GC() // materialize all statistics
237 | if err := pprof.WriteHeapProfile(bw); err != nil {
238 | log.Fatal(err)
239 | }
240 | flush()
241 | }()
242 | }
243 |
244 | if verbose {
245 | log.SetFlags(log.LstdFlags | log.Lmicroseconds)
246 | imports.Debug = true
247 | }
248 | if options.TabWidth < 0 {
249 | fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
250 | exitCode = 2
251 | return
252 | }
253 |
254 | if len(paths) == 0 {
255 | if err := processFile("", os.Stdin, os.Stdout, fromStdin); err != nil {
256 | report(err)
257 | }
258 | return
259 | }
260 |
261 | argType := singleArg
262 | if len(paths) > 1 {
263 | argType = multipleArg
264 | }
265 |
266 | for _, path := range paths {
267 | switch dir, err := os.Stat(path); {
268 | case err != nil:
269 | report(err)
270 | case dir.IsDir():
271 | walkDir(path)
272 | default:
273 | if err := processFile(path, nil, os.Stdout, argType); err != nil {
274 | report(err)
275 | }
276 | }
277 | }
278 | }
279 |
280 | func writeTempFile(dir, prefix string, data []byte) (string, error) {
281 | file, err := ioutil.TempFile(dir, prefix)
282 | if err != nil {
283 | return "", err
284 | }
285 | _, err = file.Write(data)
286 | if err1 := file.Close(); err == nil {
287 | err = err1
288 | }
289 | if err != nil {
290 | os.Remove(file.Name())
291 | return "", err
292 | }
293 | return file.Name(), nil
294 | }
295 |
296 | func diff(b1, b2 []byte, filename string) (data []byte, err error) {
297 | f1, err := writeTempFile("", "gofmt", b1)
298 | if err != nil {
299 | return
300 | }
301 | defer os.Remove(f1)
302 |
303 | f2, err := writeTempFile("", "gofmt", b2)
304 | if err != nil {
305 | return
306 | }
307 | defer os.Remove(f2)
308 |
309 | cmd := "diff"
310 | if runtime.GOOS == "plan9" {
311 | cmd = "/bin/ape/diff"
312 | }
313 |
314 | data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
315 | if len(data) > 0 {
316 | // diff exits with a non-zero status when the files don't match.
317 | // Ignore that failure as long as we get output.
318 | return replaceTempFilename(data, filename)
319 | }
320 | return
321 | }
322 |
323 | // replaceTempFilename replaces temporary filenames in diff with actual one.
324 | //
325 | // --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500
326 | // +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500
327 | // ...
328 | // ->
329 | // --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500
330 | // +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500
331 | // ...
332 | func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
333 | bs := bytes.SplitN(diff, []byte{'\n'}, 3)
334 | if len(bs) < 3 {
335 | return nil, fmt.Errorf("got unexpected diff for %s", filename)
336 | }
337 | // Preserve timestamps.
338 | var t0, t1 []byte
339 | if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
340 | t0 = bs[0][i:]
341 | }
342 | if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
343 | t1 = bs[1][i:]
344 | }
345 | // Always print filepath with slash separator.
346 | f := filepath.ToSlash(filename)
347 | bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
348 | bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
349 | return bytes.Join(bs, []byte{'\n'}), nil
350 | }
351 |
352 | // isFile reports whether name is a file.
353 | func isFile(name string) bool {
354 | fi, err := os.Stat(name)
355 | return err == nil && fi.Mode().IsRegular()
356 | }
357 |
358 | // isDir reports whether name is a directory.
359 | func isDir(name string) bool {
360 | fi, err := os.Stat(name)
361 | return err == nil && fi.IsDir()
362 | }
363 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/go/ast/astutil/imports.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package astutil contains common utilities for working with the Go AST.
6 | package astutil // import "golang.org/x/tools/go/ast/astutil"
7 |
8 | import (
9 | "fmt"
10 | "go/ast"
11 | "go/token"
12 | "strconv"
13 | "strings"
14 | )
15 |
16 | // AddImport adds the import path to the file f, if absent.
17 | func AddImport(fset *token.FileSet, f *ast.File, ipath string) (added bool) {
18 | return AddNamedImport(fset, f, "", ipath)
19 | }
20 |
21 | // AddNamedImport adds the import path to the file f, if absent.
22 | // If name is not empty, it is used to rename the import.
23 | //
24 | // For example, calling
25 | // AddNamedImport(fset, f, "pathpkg", "path")
26 | // adds
27 | // import pathpkg "path"
28 | func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added bool) {
29 | if imports(f, ipath) {
30 | return false
31 | }
32 |
33 | newImport := &ast.ImportSpec{
34 | Path: &ast.BasicLit{
35 | Kind: token.STRING,
36 | Value: strconv.Quote(ipath),
37 | },
38 | }
39 | if name != "" {
40 | newImport.Name = &ast.Ident{Name: name}
41 | }
42 |
43 | // Find an import decl to add to.
44 | // The goal is to find an existing import
45 | // whose import path has the longest shared
46 | // prefix with ipath.
47 | var (
48 | bestMatch = -1 // length of longest shared prefix
49 | lastImport = -1 // index in f.Decls of the file's final import decl
50 | impDecl *ast.GenDecl // import decl containing the best match
51 | impIndex = -1 // spec index in impDecl containing the best match
52 | )
53 | for i, decl := range f.Decls {
54 | gen, ok := decl.(*ast.GenDecl)
55 | if ok && gen.Tok == token.IMPORT {
56 | lastImport = i
57 | // Do not add to import "C", to avoid disrupting the
58 | // association with its doc comment, breaking cgo.
59 | if declImports(gen, "C") {
60 | continue
61 | }
62 |
63 | // Match an empty import decl if that's all that is available.
64 | if len(gen.Specs) == 0 && bestMatch == -1 {
65 | impDecl = gen
66 | }
67 |
68 | // Compute longest shared prefix with imports in this group.
69 | for j, spec := range gen.Specs {
70 | impspec := spec.(*ast.ImportSpec)
71 | n := matchLen(importPath(impspec), ipath)
72 | if n > bestMatch {
73 | bestMatch = n
74 | impDecl = gen
75 | impIndex = j
76 | }
77 | }
78 | }
79 | }
80 |
81 | // If no import decl found, add one after the last import.
82 | if impDecl == nil {
83 | impDecl = &ast.GenDecl{
84 | Tok: token.IMPORT,
85 | }
86 | if lastImport >= 0 {
87 | impDecl.TokPos = f.Decls[lastImport].End()
88 | } else {
89 | // There are no existing imports.
90 | // Our new import goes after the package declaration and after
91 | // the comment, if any, that starts on the same line as the
92 | // package declaration.
93 | impDecl.TokPos = f.Package
94 |
95 | file := fset.File(f.Package)
96 | pkgLine := file.Line(f.Package)
97 | for _, c := range f.Comments {
98 | if file.Line(c.Pos()) > pkgLine {
99 | break
100 | }
101 | impDecl.TokPos = c.End()
102 | }
103 | }
104 | f.Decls = append(f.Decls, nil)
105 | copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
106 | f.Decls[lastImport+1] = impDecl
107 | }
108 |
109 | // Insert new import at insertAt.
110 | insertAt := 0
111 | if impIndex >= 0 {
112 | // insert after the found import
113 | insertAt = impIndex + 1
114 | }
115 | impDecl.Specs = append(impDecl.Specs, nil)
116 | copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
117 | impDecl.Specs[insertAt] = newImport
118 | pos := impDecl.Pos()
119 | if insertAt > 0 {
120 | // If there is a comment after an existing import, preserve the comment
121 | // position by adding the new import after the comment.
122 | if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil {
123 | pos = spec.Comment.End()
124 | } else {
125 | // Assign same position as the previous import,
126 | // so that the sorter sees it as being in the same block.
127 | pos = impDecl.Specs[insertAt-1].Pos()
128 | }
129 | }
130 | if newImport.Name != nil {
131 | newImport.Name.NamePos = pos
132 | }
133 | newImport.Path.ValuePos = pos
134 | newImport.EndPos = pos
135 |
136 | // Clean up parens. impDecl contains at least one spec.
137 | if len(impDecl.Specs) == 1 {
138 | // Remove unneeded parens.
139 | impDecl.Lparen = token.NoPos
140 | } else if !impDecl.Lparen.IsValid() {
141 | // impDecl needs parens added.
142 | impDecl.Lparen = impDecl.Specs[0].Pos()
143 | }
144 |
145 | f.Imports = append(f.Imports, newImport)
146 |
147 | if len(f.Decls) <= 1 {
148 | return true
149 | }
150 |
151 | // Merge all the import declarations into the first one.
152 | var first *ast.GenDecl
153 | for i := 0; i < len(f.Decls); i++ {
154 | decl := f.Decls[i]
155 | gen, ok := decl.(*ast.GenDecl)
156 | if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
157 | continue
158 | }
159 | if first == nil {
160 | first = gen
161 | continue // Don't touch the first one.
162 | }
163 | // We now know there is more than one package in this import
164 | // declaration. Ensure that it ends up parenthesized.
165 | first.Lparen = first.Pos()
166 | // Move the imports of the other import declaration to the first one.
167 | for _, spec := range gen.Specs {
168 | spec.(*ast.ImportSpec).Path.ValuePos = first.Pos()
169 | first.Specs = append(first.Specs, spec)
170 | }
171 | f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
172 | i--
173 | }
174 |
175 | return true
176 | }
177 |
178 | // DeleteImport deletes the import path from the file f, if present.
179 | func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) {
180 | return DeleteNamedImport(fset, f, "", path)
181 | }
182 |
183 | // DeleteNamedImport deletes the import with the given name and path from the file f, if present.
184 | func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) {
185 | var delspecs []*ast.ImportSpec
186 | var delcomments []*ast.CommentGroup
187 |
188 | // Find the import nodes that import path, if any.
189 | for i := 0; i < len(f.Decls); i++ {
190 | decl := f.Decls[i]
191 | gen, ok := decl.(*ast.GenDecl)
192 | if !ok || gen.Tok != token.IMPORT {
193 | continue
194 | }
195 | for j := 0; j < len(gen.Specs); j++ {
196 | spec := gen.Specs[j]
197 | impspec := spec.(*ast.ImportSpec)
198 | if impspec.Name == nil && name != "" {
199 | continue
200 | }
201 | if impspec.Name != nil && impspec.Name.Name != name {
202 | continue
203 | }
204 | if importPath(impspec) != path {
205 | continue
206 | }
207 |
208 | // We found an import spec that imports path.
209 | // Delete it.
210 | delspecs = append(delspecs, impspec)
211 | deleted = true
212 | copy(gen.Specs[j:], gen.Specs[j+1:])
213 | gen.Specs = gen.Specs[:len(gen.Specs)-1]
214 |
215 | // If this was the last import spec in this decl,
216 | // delete the decl, too.
217 | if len(gen.Specs) == 0 {
218 | copy(f.Decls[i:], f.Decls[i+1:])
219 | f.Decls = f.Decls[:len(f.Decls)-1]
220 | i--
221 | break
222 | } else if len(gen.Specs) == 1 {
223 | if impspec.Doc != nil {
224 | delcomments = append(delcomments, impspec.Doc)
225 | }
226 | if impspec.Comment != nil {
227 | delcomments = append(delcomments, impspec.Comment)
228 | }
229 | for _, cg := range f.Comments {
230 | // Found comment on the same line as the import spec.
231 | if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line {
232 | delcomments = append(delcomments, cg)
233 | break
234 | }
235 | }
236 |
237 | spec := gen.Specs[0].(*ast.ImportSpec)
238 |
239 | // Move the documentation right after the import decl.
240 | if spec.Doc != nil {
241 | for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line {
242 | fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
243 | }
244 | }
245 | for _, cg := range f.Comments {
246 | if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line {
247 | for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line {
248 | fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
249 | }
250 | break
251 | }
252 | }
253 | }
254 | if j > 0 {
255 | lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
256 | lastLine := fset.Position(lastImpspec.Path.ValuePos).Line
257 | line := fset.Position(impspec.Path.ValuePos).Line
258 |
259 | // We deleted an entry but now there may be
260 | // a blank line-sized hole where the import was.
261 | if line-lastLine > 1 {
262 | // There was a blank line immediately preceding the deleted import,
263 | // so there's no need to close the hole.
264 | // Do nothing.
265 | } else if line != fset.File(gen.Rparen).LineCount() {
266 | // There was no blank line. Close the hole.
267 | fset.File(gen.Rparen).MergeLine(line)
268 | }
269 | }
270 | j--
271 | }
272 | }
273 |
274 | // Delete imports from f.Imports.
275 | for i := 0; i < len(f.Imports); i++ {
276 | imp := f.Imports[i]
277 | for j, del := range delspecs {
278 | if imp == del {
279 | copy(f.Imports[i:], f.Imports[i+1:])
280 | f.Imports = f.Imports[:len(f.Imports)-1]
281 | copy(delspecs[j:], delspecs[j+1:])
282 | delspecs = delspecs[:len(delspecs)-1]
283 | i--
284 | break
285 | }
286 | }
287 | }
288 |
289 | // Delete comments from f.Comments.
290 | for i := 0; i < len(f.Comments); i++ {
291 | cg := f.Comments[i]
292 | for j, del := range delcomments {
293 | if cg == del {
294 | copy(f.Comments[i:], f.Comments[i+1:])
295 | f.Comments = f.Comments[:len(f.Comments)-1]
296 | copy(delcomments[j:], delcomments[j+1:])
297 | delcomments = delcomments[:len(delcomments)-1]
298 | i--
299 | break
300 | }
301 | }
302 | }
303 |
304 | if len(delspecs) > 0 {
305 | panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs))
306 | }
307 |
308 | return
309 | }
310 |
311 | // RewriteImport rewrites any import of path oldPath to path newPath.
312 | func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {
313 | for _, imp := range f.Imports {
314 | if importPath(imp) == oldPath {
315 | rewrote = true
316 | // record old End, because the default is to compute
317 | // it using the length of imp.Path.Value.
318 | imp.EndPos = imp.End()
319 | imp.Path.Value = strconv.Quote(newPath)
320 | }
321 | }
322 | return
323 | }
324 |
325 | // UsesImport reports whether a given import is used.
326 | func UsesImport(f *ast.File, path string) (used bool) {
327 | spec := importSpec(f, path)
328 | if spec == nil {
329 | return
330 | }
331 |
332 | name := spec.Name.String()
333 | switch name {
334 | case "":
335 | // If the package name is not explicitly specified,
336 | // make an educated guess. This is not guaranteed to be correct.
337 | lastSlash := strings.LastIndex(path, "/")
338 | if lastSlash == -1 {
339 | name = path
340 | } else {
341 | name = path[lastSlash+1:]
342 | }
343 | case "_", ".":
344 | // Not sure if this import is used - err on the side of caution.
345 | return true
346 | }
347 |
348 | ast.Walk(visitFn(func(n ast.Node) {
349 | sel, ok := n.(*ast.SelectorExpr)
350 | if ok && isTopName(sel.X, name) {
351 | used = true
352 | }
353 | }), f)
354 |
355 | return
356 | }
357 |
358 | type visitFn func(node ast.Node)
359 |
360 | func (fn visitFn) Visit(node ast.Node) ast.Visitor {
361 | fn(node)
362 | return fn
363 | }
364 |
365 | // imports returns true if f imports path.
366 | func imports(f *ast.File, path string) bool {
367 | return importSpec(f, path) != nil
368 | }
369 |
370 | // importSpec returns the import spec if f imports path,
371 | // or nil otherwise.
372 | func importSpec(f *ast.File, path string) *ast.ImportSpec {
373 | for _, s := range f.Imports {
374 | if importPath(s) == path {
375 | return s
376 | }
377 | }
378 | return nil
379 | }
380 |
381 | // importPath returns the unquoted import path of s,
382 | // or "" if the path is not properly quoted.
383 | func importPath(s *ast.ImportSpec) string {
384 | t, err := strconv.Unquote(s.Path.Value)
385 | if err == nil {
386 | return t
387 | }
388 | return ""
389 | }
390 |
391 | // declImports reports whether gen contains an import of path.
392 | func declImports(gen *ast.GenDecl, path string) bool {
393 | if gen.Tok != token.IMPORT {
394 | return false
395 | }
396 | for _, spec := range gen.Specs {
397 | impspec := spec.(*ast.ImportSpec)
398 | if importPath(impspec) == path {
399 | return true
400 | }
401 | }
402 | return false
403 | }
404 |
405 | // matchLen returns the length of the longest path segment prefix shared by x and y.
406 | func matchLen(x, y string) int {
407 | n := 0
408 | for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ {
409 | if x[i] == '/' {
410 | n++
411 | }
412 | }
413 | return n
414 | }
415 |
416 | // isTopName returns true if n is a top-level unresolved identifier with the given name.
417 | func isTopName(n ast.Expr, name string) bool {
418 | id, ok := n.(*ast.Ident)
419 | return ok && id.Name == name && id.Obj == nil
420 | }
421 |
422 | // Imports returns the file imports grouped by paragraph.
423 | func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec {
424 | var groups [][]*ast.ImportSpec
425 |
426 | for _, decl := range f.Decls {
427 | genDecl, ok := decl.(*ast.GenDecl)
428 | if !ok || genDecl.Tok != token.IMPORT {
429 | break
430 | }
431 |
432 | group := []*ast.ImportSpec{}
433 |
434 | var lastLine int
435 | for _, spec := range genDecl.Specs {
436 | importSpec := spec.(*ast.ImportSpec)
437 | pos := importSpec.Path.ValuePos
438 | line := fset.Position(pos).Line
439 | if lastLine > 0 && pos > 0 && line-lastLine > 1 {
440 | groups = append(groups, group)
441 | group = []*ast.ImportSpec{}
442 | }
443 | group = append(group, importSpec)
444 | lastLine = line
445 | }
446 | groups = append(groups, group)
447 | }
448 |
449 | return groups
450 | }
451 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package astutil
6 |
7 | // This file defines utilities for working with source positions.
8 |
9 | import (
10 | "fmt"
11 | "go/ast"
12 | "go/token"
13 | "sort"
14 | )
15 |
16 | // PathEnclosingInterval returns the node that encloses the source
17 | // interval [start, end), and all its ancestors up to the AST root.
18 | //
19 | // The definition of "enclosing" used by this function considers
20 | // additional whitespace abutting a node to be enclosed by it.
21 | // In this example:
22 | //
23 | // z := x + y // add them
24 | // <-A->
25 | // <----B----->
26 | //
27 | // the ast.BinaryExpr(+) node is considered to enclose interval B
28 | // even though its [Pos()..End()) is actually only interval A.
29 | // This behaviour makes user interfaces more tolerant of imperfect
30 | // input.
31 | //
32 | // This function treats tokens as nodes, though they are not included
33 | // in the result. e.g. PathEnclosingInterval("+") returns the
34 | // enclosing ast.BinaryExpr("x + y").
35 | //
36 | // If start==end, the 1-char interval following start is used instead.
37 | //
38 | // The 'exact' result is true if the interval contains only path[0]
39 | // and perhaps some adjacent whitespace. It is false if the interval
40 | // overlaps multiple children of path[0], or if it contains only
41 | // interior whitespace of path[0].
42 | // In this example:
43 | //
44 | // z := x + y // add them
45 | // <--C--> <---E-->
46 | // ^
47 | // D
48 | //
49 | // intervals C, D and E are inexact. C is contained by the
50 | // z-assignment statement, because it spans three of its children (:=,
51 | // x, +). So too is the 1-char interval D, because it contains only
52 | // interior whitespace of the assignment. E is considered interior
53 | // whitespace of the BlockStmt containing the assignment.
54 | //
55 | // Precondition: [start, end) both lie within the same file as root.
56 | // TODO(adonovan): return (nil, false) in this case and remove precond.
57 | // Requires FileSet; see loader.tokenFileContainsPos.
58 | //
59 | // Postcondition: path is never nil; it always contains at least 'root'.
60 | //
61 | func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) {
62 | // fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging
63 |
64 | // Precondition: node.[Pos..End) and adjoining whitespace contain [start, end).
65 | var visit func(node ast.Node) bool
66 | visit = func(node ast.Node) bool {
67 | path = append(path, node)
68 |
69 | nodePos := node.Pos()
70 | nodeEnd := node.End()
71 |
72 | // fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging
73 |
74 | // Intersect [start, end) with interval of node.
75 | if start < nodePos {
76 | start = nodePos
77 | }
78 | if end > nodeEnd {
79 | end = nodeEnd
80 | }
81 |
82 | // Find sole child that contains [start, end).
83 | children := childrenOf(node)
84 | l := len(children)
85 | for i, child := range children {
86 | // [childPos, childEnd) is unaugmented interval of child.
87 | childPos := child.Pos()
88 | childEnd := child.End()
89 |
90 | // [augPos, augEnd) is whitespace-augmented interval of child.
91 | augPos := childPos
92 | augEnd := childEnd
93 | if i > 0 {
94 | augPos = children[i-1].End() // start of preceding whitespace
95 | }
96 | if i < l-1 {
97 | nextChildPos := children[i+1].Pos()
98 | // Does [start, end) lie between child and next child?
99 | if start >= augEnd && end <= nextChildPos {
100 | return false // inexact match
101 | }
102 | augEnd = nextChildPos // end of following whitespace
103 | }
104 |
105 | // fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n",
106 | // i, augPos, augEnd, start, end) // debugging
107 |
108 | // Does augmented child strictly contain [start, end)?
109 | if augPos <= start && end <= augEnd {
110 | _, isToken := child.(tokenNode)
111 | return isToken || visit(child)
112 | }
113 |
114 | // Does [start, end) overlap multiple children?
115 | // i.e. left-augmented child contains start
116 | // but LR-augmented child does not contain end.
117 | if start < childEnd && end > augEnd {
118 | break
119 | }
120 | }
121 |
122 | // No single child contained [start, end),
123 | // so node is the result. Is it exact?
124 |
125 | // (It's tempting to put this condition before the
126 | // child loop, but it gives the wrong result in the
127 | // case where a node (e.g. ExprStmt) and its sole
128 | // child have equal intervals.)
129 | if start == nodePos && end == nodeEnd {
130 | return true // exact match
131 | }
132 |
133 | return false // inexact: overlaps multiple children
134 | }
135 |
136 | if start > end {
137 | start, end = end, start
138 | }
139 |
140 | if start < root.End() && end > root.Pos() {
141 | if start == end {
142 | end = start + 1 // empty interval => interval of size 1
143 | }
144 | exact = visit(root)
145 |
146 | // Reverse the path:
147 | for i, l := 0, len(path); i < l/2; i++ {
148 | path[i], path[l-1-i] = path[l-1-i], path[i]
149 | }
150 | } else {
151 | // Selection lies within whitespace preceding the
152 | // first (or following the last) declaration in the file.
153 | // The result nonetheless always includes the ast.File.
154 | path = append(path, root)
155 | }
156 |
157 | return
158 | }
159 |
160 | // tokenNode is a dummy implementation of ast.Node for a single token.
161 | // They are used transiently by PathEnclosingInterval but never escape
162 | // this package.
163 | //
164 | type tokenNode struct {
165 | pos token.Pos
166 | end token.Pos
167 | }
168 |
169 | func (n tokenNode) Pos() token.Pos {
170 | return n.pos
171 | }
172 |
173 | func (n tokenNode) End() token.Pos {
174 | return n.end
175 | }
176 |
177 | func tok(pos token.Pos, len int) ast.Node {
178 | return tokenNode{pos, pos + token.Pos(len)}
179 | }
180 |
181 | // childrenOf returns the direct non-nil children of ast.Node n.
182 | // It may include fake ast.Node implementations for bare tokens.
183 | // it is not safe to call (e.g.) ast.Walk on such nodes.
184 | //
185 | func childrenOf(n ast.Node) []ast.Node {
186 | var children []ast.Node
187 |
188 | // First add nodes for all true subtrees.
189 | ast.Inspect(n, func(node ast.Node) bool {
190 | if node == n { // push n
191 | return true // recur
192 | }
193 | if node != nil { // push child
194 | children = append(children, node)
195 | }
196 | return false // no recursion
197 | })
198 |
199 | // Then add fake Nodes for bare tokens.
200 | switch n := n.(type) {
201 | case *ast.ArrayType:
202 | children = append(children,
203 | tok(n.Lbrack, len("[")),
204 | tok(n.Elt.End(), len("]")))
205 |
206 | case *ast.AssignStmt:
207 | children = append(children,
208 | tok(n.TokPos, len(n.Tok.String())))
209 |
210 | case *ast.BasicLit:
211 | children = append(children,
212 | tok(n.ValuePos, len(n.Value)))
213 |
214 | case *ast.BinaryExpr:
215 | children = append(children, tok(n.OpPos, len(n.Op.String())))
216 |
217 | case *ast.BlockStmt:
218 | children = append(children,
219 | tok(n.Lbrace, len("{")),
220 | tok(n.Rbrace, len("}")))
221 |
222 | case *ast.BranchStmt:
223 | children = append(children,
224 | tok(n.TokPos, len(n.Tok.String())))
225 |
226 | case *ast.CallExpr:
227 | children = append(children,
228 | tok(n.Lparen, len("(")),
229 | tok(n.Rparen, len(")")))
230 | if n.Ellipsis != 0 {
231 | children = append(children, tok(n.Ellipsis, len("...")))
232 | }
233 |
234 | case *ast.CaseClause:
235 | if n.List == nil {
236 | children = append(children,
237 | tok(n.Case, len("default")))
238 | } else {
239 | children = append(children,
240 | tok(n.Case, len("case")))
241 | }
242 | children = append(children, tok(n.Colon, len(":")))
243 |
244 | case *ast.ChanType:
245 | switch n.Dir {
246 | case ast.RECV:
247 | children = append(children, tok(n.Begin, len("<-chan")))
248 | case ast.SEND:
249 | children = append(children, tok(n.Begin, len("chan<-")))
250 | case ast.RECV | ast.SEND:
251 | children = append(children, tok(n.Begin, len("chan")))
252 | }
253 |
254 | case *ast.CommClause:
255 | if n.Comm == nil {
256 | children = append(children,
257 | tok(n.Case, len("default")))
258 | } else {
259 | children = append(children,
260 | tok(n.Case, len("case")))
261 | }
262 | children = append(children, tok(n.Colon, len(":")))
263 |
264 | case *ast.Comment:
265 | // nop
266 |
267 | case *ast.CommentGroup:
268 | // nop
269 |
270 | case *ast.CompositeLit:
271 | children = append(children,
272 | tok(n.Lbrace, len("{")),
273 | tok(n.Rbrace, len("{")))
274 |
275 | case *ast.DeclStmt:
276 | // nop
277 |
278 | case *ast.DeferStmt:
279 | children = append(children,
280 | tok(n.Defer, len("defer")))
281 |
282 | case *ast.Ellipsis:
283 | children = append(children,
284 | tok(n.Ellipsis, len("...")))
285 |
286 | case *ast.EmptyStmt:
287 | // nop
288 |
289 | case *ast.ExprStmt:
290 | // nop
291 |
292 | case *ast.Field:
293 | // TODO(adonovan): Field.{Doc,Comment,Tag}?
294 |
295 | case *ast.FieldList:
296 | children = append(children,
297 | tok(n.Opening, len("(")),
298 | tok(n.Closing, len(")")))
299 |
300 | case *ast.File:
301 | // TODO test: Doc
302 | children = append(children,
303 | tok(n.Package, len("package")))
304 |
305 | case *ast.ForStmt:
306 | children = append(children,
307 | tok(n.For, len("for")))
308 |
309 | case *ast.FuncDecl:
310 | // TODO(adonovan): FuncDecl.Comment?
311 |
312 | // Uniquely, FuncDecl breaks the invariant that
313 | // preorder traversal yields tokens in lexical order:
314 | // in fact, FuncDecl.Recv precedes FuncDecl.Type.Func.
315 | //
316 | // As a workaround, we inline the case for FuncType
317 | // here and order things correctly.
318 | //
319 | children = nil // discard ast.Walk(FuncDecl) info subtrees
320 | children = append(children, tok(n.Type.Func, len("func")))
321 | if n.Recv != nil {
322 | children = append(children, n.Recv)
323 | }
324 | children = append(children, n.Name)
325 | if n.Type.Params != nil {
326 | children = append(children, n.Type.Params)
327 | }
328 | if n.Type.Results != nil {
329 | children = append(children, n.Type.Results)
330 | }
331 | if n.Body != nil {
332 | children = append(children, n.Body)
333 | }
334 |
335 | case *ast.FuncLit:
336 | // nop
337 |
338 | case *ast.FuncType:
339 | if n.Func != 0 {
340 | children = append(children,
341 | tok(n.Func, len("func")))
342 | }
343 |
344 | case *ast.GenDecl:
345 | children = append(children,
346 | tok(n.TokPos, len(n.Tok.String())))
347 | if n.Lparen != 0 {
348 | children = append(children,
349 | tok(n.Lparen, len("(")),
350 | tok(n.Rparen, len(")")))
351 | }
352 |
353 | case *ast.GoStmt:
354 | children = append(children,
355 | tok(n.Go, len("go")))
356 |
357 | case *ast.Ident:
358 | children = append(children,
359 | tok(n.NamePos, len(n.Name)))
360 |
361 | case *ast.IfStmt:
362 | children = append(children,
363 | tok(n.If, len("if")))
364 |
365 | case *ast.ImportSpec:
366 | // TODO(adonovan): ImportSpec.{Doc,EndPos}?
367 |
368 | case *ast.IncDecStmt:
369 | children = append(children,
370 | tok(n.TokPos, len(n.Tok.String())))
371 |
372 | case *ast.IndexExpr:
373 | children = append(children,
374 | tok(n.Lbrack, len("{")),
375 | tok(n.Rbrack, len("}")))
376 |
377 | case *ast.InterfaceType:
378 | children = append(children,
379 | tok(n.Interface, len("interface")))
380 |
381 | case *ast.KeyValueExpr:
382 | children = append(children,
383 | tok(n.Colon, len(":")))
384 |
385 | case *ast.LabeledStmt:
386 | children = append(children,
387 | tok(n.Colon, len(":")))
388 |
389 | case *ast.MapType:
390 | children = append(children,
391 | tok(n.Map, len("map")))
392 |
393 | case *ast.ParenExpr:
394 | children = append(children,
395 | tok(n.Lparen, len("(")),
396 | tok(n.Rparen, len(")")))
397 |
398 | case *ast.RangeStmt:
399 | children = append(children,
400 | tok(n.For, len("for")),
401 | tok(n.TokPos, len(n.Tok.String())))
402 |
403 | case *ast.ReturnStmt:
404 | children = append(children,
405 | tok(n.Return, len("return")))
406 |
407 | case *ast.SelectStmt:
408 | children = append(children,
409 | tok(n.Select, len("select")))
410 |
411 | case *ast.SelectorExpr:
412 | // nop
413 |
414 | case *ast.SendStmt:
415 | children = append(children,
416 | tok(n.Arrow, len("<-")))
417 |
418 | case *ast.SliceExpr:
419 | children = append(children,
420 | tok(n.Lbrack, len("[")),
421 | tok(n.Rbrack, len("]")))
422 |
423 | case *ast.StarExpr:
424 | children = append(children, tok(n.Star, len("*")))
425 |
426 | case *ast.StructType:
427 | children = append(children, tok(n.Struct, len("struct")))
428 |
429 | case *ast.SwitchStmt:
430 | children = append(children, tok(n.Switch, len("switch")))
431 |
432 | case *ast.TypeAssertExpr:
433 | children = append(children,
434 | tok(n.Lparen-1, len(".")),
435 | tok(n.Lparen, len("(")),
436 | tok(n.Rparen, len(")")))
437 |
438 | case *ast.TypeSpec:
439 | // TODO(adonovan): TypeSpec.{Doc,Comment}?
440 |
441 | case *ast.TypeSwitchStmt:
442 | children = append(children, tok(n.Switch, len("switch")))
443 |
444 | case *ast.UnaryExpr:
445 | children = append(children, tok(n.OpPos, len(n.Op.String())))
446 |
447 | case *ast.ValueSpec:
448 | // TODO(adonovan): ValueSpec.{Doc,Comment}?
449 |
450 | case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt:
451 | // nop
452 | }
453 |
454 | // TODO(adonovan): opt: merge the logic of ast.Inspect() into
455 | // the switch above so we can make interleaved callbacks for
456 | // both Nodes and Tokens in the right order and avoid the need
457 | // to sort.
458 | sort.Sort(byPos(children))
459 |
460 | return children
461 | }
462 |
463 | type byPos []ast.Node
464 |
465 | func (sl byPos) Len() int {
466 | return len(sl)
467 | }
468 | func (sl byPos) Less(i, j int) bool {
469 | return sl[i].Pos() < sl[j].Pos()
470 | }
471 | func (sl byPos) Swap(i, j int) {
472 | sl[i], sl[j] = sl[j], sl[i]
473 | }
474 |
475 | // NodeDescription returns a description of the concrete type of n suitable
476 | // for a user interface.
477 | //
478 | // TODO(adonovan): in some cases (e.g. Field, FieldList, Ident,
479 | // StarExpr) we could be much more specific given the path to the AST
480 | // root. Perhaps we should do that.
481 | //
482 | func NodeDescription(n ast.Node) string {
483 | switch n := n.(type) {
484 | case *ast.ArrayType:
485 | return "array type"
486 | case *ast.AssignStmt:
487 | return "assignment"
488 | case *ast.BadDecl:
489 | return "bad declaration"
490 | case *ast.BadExpr:
491 | return "bad expression"
492 | case *ast.BadStmt:
493 | return "bad statement"
494 | case *ast.BasicLit:
495 | return "basic literal"
496 | case *ast.BinaryExpr:
497 | return fmt.Sprintf("binary %s operation", n.Op)
498 | case *ast.BlockStmt:
499 | return "block"
500 | case *ast.BranchStmt:
501 | switch n.Tok {
502 | case token.BREAK:
503 | return "break statement"
504 | case token.CONTINUE:
505 | return "continue statement"
506 | case token.GOTO:
507 | return "goto statement"
508 | case token.FALLTHROUGH:
509 | return "fall-through statement"
510 | }
511 | case *ast.CallExpr:
512 | if len(n.Args) == 1 && !n.Ellipsis.IsValid() {
513 | return "function call (or conversion)"
514 | }
515 | return "function call"
516 | case *ast.CaseClause:
517 | return "case clause"
518 | case *ast.ChanType:
519 | return "channel type"
520 | case *ast.CommClause:
521 | return "communication clause"
522 | case *ast.Comment:
523 | return "comment"
524 | case *ast.CommentGroup:
525 | return "comment group"
526 | case *ast.CompositeLit:
527 | return "composite literal"
528 | case *ast.DeclStmt:
529 | return NodeDescription(n.Decl) + " statement"
530 | case *ast.DeferStmt:
531 | return "defer statement"
532 | case *ast.Ellipsis:
533 | return "ellipsis"
534 | case *ast.EmptyStmt:
535 | return "empty statement"
536 | case *ast.ExprStmt:
537 | return "expression statement"
538 | case *ast.Field:
539 | // Can be any of these:
540 | // struct {x, y int} -- struct field(s)
541 | // struct {T} -- anon struct field
542 | // interface {I} -- interface embedding
543 | // interface {f()} -- interface method
544 | // func (A) func(B) C -- receiver, param(s), result(s)
545 | return "field/method/parameter"
546 | case *ast.FieldList:
547 | return "field/method/parameter list"
548 | case *ast.File:
549 | return "source file"
550 | case *ast.ForStmt:
551 | return "for loop"
552 | case *ast.FuncDecl:
553 | return "function declaration"
554 | case *ast.FuncLit:
555 | return "function literal"
556 | case *ast.FuncType:
557 | return "function type"
558 | case *ast.GenDecl:
559 | switch n.Tok {
560 | case token.IMPORT:
561 | return "import declaration"
562 | case token.CONST:
563 | return "constant declaration"
564 | case token.TYPE:
565 | return "type declaration"
566 | case token.VAR:
567 | return "variable declaration"
568 | }
569 | case *ast.GoStmt:
570 | return "go statement"
571 | case *ast.Ident:
572 | return "identifier"
573 | case *ast.IfStmt:
574 | return "if statement"
575 | case *ast.ImportSpec:
576 | return "import specification"
577 | case *ast.IncDecStmt:
578 | if n.Tok == token.INC {
579 | return "increment statement"
580 | }
581 | return "decrement statement"
582 | case *ast.IndexExpr:
583 | return "index expression"
584 | case *ast.InterfaceType:
585 | return "interface type"
586 | case *ast.KeyValueExpr:
587 | return "key/value association"
588 | case *ast.LabeledStmt:
589 | return "statement label"
590 | case *ast.MapType:
591 | return "map type"
592 | case *ast.Package:
593 | return "package"
594 | case *ast.ParenExpr:
595 | return "parenthesized " + NodeDescription(n.X)
596 | case *ast.RangeStmt:
597 | return "range loop"
598 | case *ast.ReturnStmt:
599 | return "return statement"
600 | case *ast.SelectStmt:
601 | return "select statement"
602 | case *ast.SelectorExpr:
603 | return "selector"
604 | case *ast.SendStmt:
605 | return "channel send"
606 | case *ast.SliceExpr:
607 | return "slice expression"
608 | case *ast.StarExpr:
609 | return "*-operation" // load/store expr or pointer type
610 | case *ast.StructType:
611 | return "struct type"
612 | case *ast.SwitchStmt:
613 | return "switch statement"
614 | case *ast.TypeAssertExpr:
615 | return "type assertion"
616 | case *ast.TypeSpec:
617 | return "type specification"
618 | case *ast.TypeSwitchStmt:
619 | return "type switch"
620 | case *ast.UnaryExpr:
621 | return fmt.Sprintf("unary %s operation", n.Op)
622 | case *ast.ValueSpec:
623 | return "value specification"
624 |
625 | }
626 | panic(fmt.Sprintf("unexpected node type: %T", n))
627 | }
628 |
--------------------------------------------------------------------------------
/vendor/golang.org/x/tools/imports/fix.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package imports
6 |
7 | import (
8 | "bufio"
9 | "bytes"
10 | "fmt"
11 | "go/ast"
12 | "go/build"
13 | "go/parser"
14 | "go/token"
15 | "io/ioutil"
16 | "log"
17 | "os"
18 | "path"
19 | "path/filepath"
20 | "sort"
21 | "strings"
22 | "sync"
23 |
24 | "golang.org/x/tools/go/ast/astutil"
25 | )
26 |
27 | // Debug controls verbose logging.
28 | var Debug = false
29 |
30 | var (
31 | inTests = false // set true by fix_test.go; if false, no need to use testMu
32 | testMu sync.RWMutex // guards globals reset by tests; used only if inTests
33 | )
34 |
35 | // LocalPrefix, if set, instructs Process to sort import paths with the given
36 | // prefix into another group after 3rd-party packages.
37 | var LocalPrefix string
38 |
39 | // importToGroup is a list of functions which map from an import path to
40 | // a group number.
41 | var importToGroup = []func(importPath string) (num int, ok bool){
42 | func(importPath string) (num int, ok bool) {
43 | if LocalPrefix != "" && strings.HasPrefix(importPath, LocalPrefix) {
44 | return 3, true
45 | }
46 | return
47 | },
48 | func(importPath string) (num int, ok bool) {
49 | if strings.HasPrefix(importPath, "appengine") {
50 | return 2, true
51 | }
52 | return
53 | },
54 | func(importPath string) (num int, ok bool) {
55 | if strings.Contains(importPath, ".") {
56 | return 1, true
57 | }
58 | return
59 | },
60 | }
61 |
62 | func importGroup(importPath string) int {
63 | for _, fn := range importToGroup {
64 | if n, ok := fn(importPath); ok {
65 | return n
66 | }
67 | }
68 | return 0
69 | }
70 |
71 | // importInfo is a summary of information about one import.
72 | type importInfo struct {
73 | Path string // full import path (e.g. "crypto/rand")
74 | Alias string // import alias, if present (e.g. "crand")
75 | }
76 |
77 | // packageInfo is a summary of features found in a package.
78 | type packageInfo struct {
79 | Globals map[string]bool // symbol => true
80 | Imports map[string]importInfo // pkg base name or alias => info
81 | }
82 |
83 | // dirPackageInfo exposes the dirPackageInfoFile function so that it can be overridden.
84 | var dirPackageInfo = dirPackageInfoFile
85 |
86 | // dirPackageInfoFile gets information from other files in the package.
87 | func dirPackageInfoFile(pkgName, srcDir, filename string) (*packageInfo, error) {
88 | considerTests := strings.HasSuffix(filename, "_test.go")
89 |
90 | // Handle file from stdin
91 | if _, err := os.Stat(filename); err != nil {
92 | if os.IsNotExist(err) {
93 | return &packageInfo{}, nil
94 | }
95 | return nil, err
96 | }
97 |
98 | fileBase := filepath.Base(filename)
99 | packageFileInfos, err := ioutil.ReadDir(srcDir)
100 | if err != nil {
101 | return nil, err
102 | }
103 |
104 | info := &packageInfo{Globals: make(map[string]bool), Imports: make(map[string]importInfo)}
105 | for _, fi := range packageFileInfos {
106 | if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") {
107 | continue
108 | }
109 | if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") {
110 | continue
111 | }
112 |
113 | fileSet := token.NewFileSet()
114 | root, err := parser.ParseFile(fileSet, filepath.Join(srcDir, fi.Name()), nil, 0)
115 | if err != nil {
116 | continue
117 | }
118 |
119 | for _, decl := range root.Decls {
120 | genDecl, ok := decl.(*ast.GenDecl)
121 | if !ok {
122 | continue
123 | }
124 |
125 | for _, spec := range genDecl.Specs {
126 | valueSpec, ok := spec.(*ast.ValueSpec)
127 | if !ok {
128 | continue
129 | }
130 | info.Globals[valueSpec.Names[0].Name] = true
131 | }
132 | }
133 |
134 | for _, imp := range root.Imports {
135 | impInfo := importInfo{Path: strings.Trim(imp.Path.Value, `"`)}
136 | name := path.Base(impInfo.Path)
137 | if imp.Name != nil {
138 | name = strings.Trim(imp.Name.Name, `"`)
139 | impInfo.Alias = name
140 | }
141 | info.Imports[name] = impInfo
142 | }
143 | }
144 | return info, nil
145 | }
146 |
147 | func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) {
148 | // refs are a set of possible package references currently unsatisfied by imports.
149 | // first key: either base package (e.g. "fmt") or renamed package
150 | // second key: referenced package symbol (e.g. "Println")
151 | refs := make(map[string]map[string]bool)
152 |
153 | // decls are the current package imports. key is base package or renamed package.
154 | decls := make(map[string]*ast.ImportSpec)
155 |
156 | abs, err := filepath.Abs(filename)
157 | if err != nil {
158 | return nil, err
159 | }
160 | srcDir := filepath.Dir(abs)
161 | if Debug {
162 | log.Printf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir)
163 | }
164 |
165 | var packageInfo *packageInfo
166 | var loadedPackageInfo bool
167 |
168 | // collect potential uses of packages.
169 | var visitor visitFn
170 | visitor = visitFn(func(node ast.Node) ast.Visitor {
171 | if node == nil {
172 | return visitor
173 | }
174 | switch v := node.(type) {
175 | case *ast.ImportSpec:
176 | if v.Name != nil {
177 | decls[v.Name.Name] = v
178 | break
179 | }
180 | ipath := strings.Trim(v.Path.Value, `"`)
181 | if ipath == "C" {
182 | break
183 | }
184 | local := importPathToName(ipath, srcDir)
185 | decls[local] = v
186 | case *ast.SelectorExpr:
187 | xident, ok := v.X.(*ast.Ident)
188 | if !ok {
189 | break
190 | }
191 | if xident.Obj != nil {
192 | // if the parser can resolve it, it's not a package ref
193 | break
194 | }
195 | pkgName := xident.Name
196 | if refs[pkgName] == nil {
197 | refs[pkgName] = make(map[string]bool)
198 | }
199 | if !loadedPackageInfo {
200 | loadedPackageInfo = true
201 | packageInfo, _ = dirPackageInfo(f.Name.Name, srcDir, filename)
202 | }
203 | if decls[pkgName] == nil && (packageInfo == nil || !packageInfo.Globals[pkgName]) {
204 | refs[pkgName][v.Sel.Name] = true
205 | }
206 | }
207 | return visitor
208 | })
209 | ast.Walk(visitor, f)
210 |
211 | // Nil out any unused ImportSpecs, to be removed in following passes
212 | unusedImport := map[string]string{}
213 | for pkg, is := range decls {
214 | if refs[pkg] == nil && pkg != "_" && pkg != "." {
215 | name := ""
216 | if is.Name != nil {
217 | name = is.Name.Name
218 | }
219 | unusedImport[strings.Trim(is.Path.Value, `"`)] = name
220 | }
221 | }
222 | for ipath, name := range unusedImport {
223 | if ipath == "C" {
224 | // Don't remove cgo stuff.
225 | continue
226 | }
227 | astutil.DeleteNamedImport(fset, f, name, ipath)
228 | }
229 |
230 | for pkgName, symbols := range refs {
231 | if len(symbols) == 0 {
232 | // skip over packages already imported
233 | delete(refs, pkgName)
234 | }
235 | }
236 |
237 | // Fast path, all references already imported.
238 | if len(refs) == 0 {
239 | return nil, nil
240 | }
241 |
242 | // Can assume this will be necessary in all cases now.
243 | if !loadedPackageInfo {
244 | packageInfo, _ = dirPackageInfo(f.Name.Name, srcDir, filename)
245 | }
246 |
247 | // Search for imports matching potential package references.
248 | searches := 0
249 | type result struct {
250 | ipath string // import path (if err == nil)
251 | name string // optional name to rename import as
252 | err error
253 | }
254 | results := make(chan result)
255 | for pkgName, symbols := range refs {
256 | go func(pkgName string, symbols map[string]bool) {
257 | sibling := packageInfo.Imports[pkgName]
258 | if sibling.Path != "" {
259 | results <- result{ipath: sibling.Path, name: sibling.Alias}
260 | return
261 | }
262 | ipath, rename, err := findImport(pkgName, symbols, filename)
263 | r := result{ipath: ipath, err: err}
264 | if rename {
265 | r.name = pkgName
266 | }
267 | results <- r
268 | }(pkgName, symbols)
269 | searches++
270 | }
271 | for i := 0; i < searches; i++ {
272 | result := <-results
273 | if result.err != nil {
274 | return nil, result.err
275 | }
276 | if result.ipath != "" {
277 | if result.name != "" {
278 | astutil.AddNamedImport(fset, f, result.name, result.ipath)
279 | } else {
280 | astutil.AddImport(fset, f, result.ipath)
281 | }
282 | added = append(added, result.ipath)
283 | }
284 | }
285 |
286 | return added, nil
287 | }
288 |
289 | // importPathToName returns the package name for the given import path.
290 | var importPathToName func(importPath, srcDir string) (packageName string) = importPathToNameGoPath
291 |
292 | // importPathToNameBasic assumes the package name is the base of import path.
293 | func importPathToNameBasic(importPath, srcDir string) (packageName string) {
294 | return path.Base(importPath)
295 | }
296 |
297 | // importPathToNameGoPath finds out the actual package name, as declared in its .go files.
298 | // If there's a problem, it falls back to using importPathToNameBasic.
299 | func importPathToNameGoPath(importPath, srcDir string) (packageName string) {
300 | // Fast path for standard library without going to disk.
301 | if pkg, ok := stdImportPackage[importPath]; ok {
302 | return pkg
303 | }
304 |
305 | pkgName, err := importPathToNameGoPathParse(importPath, srcDir)
306 | if Debug {
307 | log.Printf("importPathToNameGoPathParse(%q, srcDir=%q) = %q, %v", importPath, srcDir, pkgName, err)
308 | }
309 | if err == nil {
310 | return pkgName
311 | }
312 | return importPathToNameBasic(importPath, srcDir)
313 | }
314 |
315 | // importPathToNameGoPathParse is a faster version of build.Import if
316 | // the only thing desired is the package name. It uses build.FindOnly
317 | // to find the directory and then only parses one file in the package,
318 | // trusting that the files in the directory are consistent.
319 | func importPathToNameGoPathParse(importPath, srcDir string) (packageName string, err error) {
320 | buildPkg, err := build.Import(importPath, srcDir, build.FindOnly)
321 | if err != nil {
322 | return "", err
323 | }
324 | d, err := os.Open(buildPkg.Dir)
325 | if err != nil {
326 | return "", err
327 | }
328 | names, err := d.Readdirnames(-1)
329 | d.Close()
330 | if err != nil {
331 | return "", err
332 | }
333 | sort.Strings(names) // to have predictable behavior
334 | var lastErr error
335 | var nfile int
336 | for _, name := range names {
337 | if !strings.HasSuffix(name, ".go") {
338 | continue
339 | }
340 | if strings.HasSuffix(name, "_test.go") {
341 | continue
342 | }
343 | nfile++
344 | fullFile := filepath.Join(buildPkg.Dir, name)
345 |
346 | fset := token.NewFileSet()
347 | f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly)
348 | if err != nil {
349 | lastErr = err
350 | continue
351 | }
352 | pkgName := f.Name.Name
353 | if pkgName == "documentation" {
354 | // Special case from go/build.ImportDir, not
355 | // handled by ctx.MatchFile.
356 | continue
357 | }
358 | if pkgName == "main" {
359 | // Also skip package main, assuming it's a +build ignore generator or example.
360 | // Since you can't import a package main anyway, there's no harm here.
361 | continue
362 | }
363 | return pkgName, nil
364 | }
365 | if lastErr != nil {
366 | return "", lastErr
367 | }
368 | return "", fmt.Errorf("no importable package found in %d Go files", nfile)
369 | }
370 |
371 | var stdImportPackage = map[string]string{} // "net/http" => "http"
372 |
373 | func init() {
374 | // Nothing in the standard library has a package name not
375 | // matching its import base name.
376 | for _, pkg := range stdlib {
377 | if _, ok := stdImportPackage[pkg]; !ok {
378 | stdImportPackage[pkg] = path.Base(pkg)
379 | }
380 | }
381 | }
382 |
383 | // Directory-scanning state.
384 | var (
385 | // scanGoRootOnce guards calling scanGoRoot (for $GOROOT)
386 | scanGoRootOnce sync.Once
387 | // scanGoPathOnce guards calling scanGoPath (for $GOPATH)
388 | scanGoPathOnce sync.Once
389 |
390 | // populateIgnoreOnce guards calling populateIgnore
391 | populateIgnoreOnce sync.Once
392 | ignoredDirs []os.FileInfo
393 |
394 | dirScanMu sync.RWMutex
395 | dirScan map[string]*pkg // abs dir path => *pkg
396 | )
397 |
398 | type pkg struct {
399 | dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http")
400 | importPath string // full pkg import path ("net/http", "foo/bar/vendor/a/b")
401 | importPathShort string // vendorless import path ("net/http", "a/b")
402 | }
403 |
404 | // byImportPathShortLength sorts by the short import path length, breaking ties on the
405 | // import string itself.
406 | type byImportPathShortLength []*pkg
407 |
408 | func (s byImportPathShortLength) Len() int { return len(s) }
409 | func (s byImportPathShortLength) Less(i, j int) bool {
410 | vi, vj := s[i].importPathShort, s[j].importPathShort
411 | return len(vi) < len(vj) || (len(vi) == len(vj) && vi < vj)
412 |
413 | }
414 | func (s byImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
415 |
416 | // guarded by populateIgnoreOnce; populates ignoredDirs.
417 | func populateIgnore() {
418 | for _, srcDir := range build.Default.SrcDirs() {
419 | if srcDir == filepath.Join(build.Default.GOROOT, "src") {
420 | continue
421 | }
422 | populateIgnoredDirs(srcDir)
423 | }
424 | }
425 |
426 | // populateIgnoredDirs reads an optional config file at /.goimportsignore
427 | // of relative directories to ignore when scanning for go files.
428 | // The provided path is one of the $GOPATH entries with "src" appended.
429 | func populateIgnoredDirs(path string) {
430 | file := filepath.Join(path, ".goimportsignore")
431 | slurp, err := ioutil.ReadFile(file)
432 | if Debug {
433 | if err != nil {
434 | log.Print(err)
435 | } else {
436 | log.Printf("Read %s", file)
437 | }
438 | }
439 | if err != nil {
440 | return
441 | }
442 | bs := bufio.NewScanner(bytes.NewReader(slurp))
443 | for bs.Scan() {
444 | line := strings.TrimSpace(bs.Text())
445 | if line == "" || strings.HasPrefix(line, "#") {
446 | continue
447 | }
448 | full := filepath.Join(path, line)
449 | if fi, err := os.Stat(full); err == nil {
450 | ignoredDirs = append(ignoredDirs, fi)
451 | if Debug {
452 | log.Printf("Directory added to ignore list: %s", full)
453 | }
454 | } else if Debug {
455 | log.Printf("Error statting entry in .goimportsignore: %v", err)
456 | }
457 | }
458 | }
459 |
460 | func skipDir(fi os.FileInfo) bool {
461 | for _, ignoredDir := range ignoredDirs {
462 | if os.SameFile(fi, ignoredDir) {
463 | return true
464 | }
465 | }
466 | return false
467 | }
468 |
469 | // shouldTraverse reports whether the symlink fi should, found in dir,
470 | // should be followed. It makes sure symlinks were never visited
471 | // before to avoid symlink loops.
472 | func shouldTraverse(dir string, fi os.FileInfo) bool {
473 | path := filepath.Join(dir, fi.Name())
474 | target, err := filepath.EvalSymlinks(path)
475 | if err != nil {
476 | if !os.IsNotExist(err) {
477 | fmt.Fprintln(os.Stderr, err)
478 | }
479 | return false
480 | }
481 | ts, err := os.Stat(target)
482 | if err != nil {
483 | fmt.Fprintln(os.Stderr, err)
484 | return false
485 | }
486 | if !ts.IsDir() {
487 | return false
488 | }
489 | if skipDir(ts) {
490 | return false
491 | }
492 | // Check for symlink loops by statting each directory component
493 | // and seeing if any are the same file as ts.
494 | for {
495 | parent := filepath.Dir(path)
496 | if parent == path {
497 | // Made it to the root without seeing a cycle.
498 | // Use this symlink.
499 | return true
500 | }
501 | parentInfo, err := os.Stat(parent)
502 | if err != nil {
503 | return false
504 | }
505 | if os.SameFile(ts, parentInfo) {
506 | // Cycle. Don't traverse.
507 | return false
508 | }
509 | path = parent
510 | }
511 |
512 | }
513 |
514 | var testHookScanDir = func(dir string) {}
515 |
516 | var scanGoRootDone = make(chan struct{}) // closed when scanGoRoot is done
517 |
518 | func scanGoRoot() {
519 | go func() {
520 | scanGoDirs(true)
521 | close(scanGoRootDone)
522 | }()
523 | }
524 |
525 | func scanGoPath() { scanGoDirs(false) }
526 |
527 | func scanGoDirs(goRoot bool) {
528 | if Debug {
529 | which := "$GOROOT"
530 | if !goRoot {
531 | which = "$GOPATH"
532 | }
533 | log.Printf("scanning " + which)
534 | defer log.Printf("scanned " + which)
535 | }
536 | dirScanMu.Lock()
537 | if dirScan == nil {
538 | dirScan = make(map[string]*pkg)
539 | }
540 | dirScanMu.Unlock()
541 |
542 | for _, srcDir := range build.Default.SrcDirs() {
543 | isGoroot := srcDir == filepath.Join(build.Default.GOROOT, "src")
544 | if isGoroot != goRoot {
545 | continue
546 | }
547 | testHookScanDir(srcDir)
548 | walkFn := func(path string, typ os.FileMode) error {
549 | dir := filepath.Dir(path)
550 | if typ.IsRegular() {
551 | if dir == srcDir {
552 | // Doesn't make sense to have regular files
553 | // directly in your $GOPATH/src or $GOROOT/src.
554 | return nil
555 | }
556 | if !strings.HasSuffix(path, ".go") {
557 | return nil
558 | }
559 | dirScanMu.Lock()
560 | if _, dup := dirScan[dir]; !dup {
561 | importpath := filepath.ToSlash(dir[len(srcDir)+len("/"):])
562 | dirScan[dir] = &pkg{
563 | importPath: importpath,
564 | importPathShort: vendorlessImportPath(importpath),
565 | dir: dir,
566 | }
567 | }
568 | dirScanMu.Unlock()
569 | return nil
570 | }
571 | if typ == os.ModeDir {
572 | base := filepath.Base(path)
573 | if base == "" || base[0] == '.' || base[0] == '_' ||
574 | base == "testdata" || base == "node_modules" {
575 | return filepath.SkipDir
576 | }
577 | fi, err := os.Lstat(path)
578 | if err == nil && skipDir(fi) {
579 | if Debug {
580 | log.Printf("skipping directory %q under %s", fi.Name(), dir)
581 | }
582 | return filepath.SkipDir
583 | }
584 | return nil
585 | }
586 | if typ == os.ModeSymlink {
587 | base := filepath.Base(path)
588 | if strings.HasPrefix(base, ".#") {
589 | // Emacs noise.
590 | return nil
591 | }
592 | fi, err := os.Lstat(path)
593 | if err != nil {
594 | // Just ignore it.
595 | return nil
596 | }
597 | if shouldTraverse(dir, fi) {
598 | return traverseLink
599 | }
600 | }
601 | return nil
602 | }
603 | if err := fastWalk(srcDir, walkFn); err != nil {
604 | log.Printf("goimports: scanning directory %v: %v", srcDir, err)
605 | }
606 | }
607 | }
608 |
609 | // vendorlessImportPath returns the devendorized version of the provided import path.
610 | // e.g. "foo/bar/vendor/a/b" => "a/b"
611 | func vendorlessImportPath(ipath string) string {
612 | // Devendorize for use in import statement.
613 | if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 {
614 | return ipath[i+len("/vendor/"):]
615 | }
616 | if strings.HasPrefix(ipath, "vendor/") {
617 | return ipath[len("vendor/"):]
618 | }
619 | return ipath
620 | }
621 |
622 | // loadExports returns the set of exported symbols in the package at dir.
623 | // It returns nil on error or if the package name in dir does not match expectPackage.
624 | var loadExports func(expectPackage, dir string) map[string]bool = loadExportsGoPath
625 |
626 | func loadExportsGoPath(expectPackage, dir string) map[string]bool {
627 | if Debug {
628 | log.Printf("loading exports in dir %s (seeking package %s)", dir, expectPackage)
629 | }
630 | exports := make(map[string]bool)
631 |
632 | ctx := build.Default
633 |
634 | // ReadDir is like ioutil.ReadDir, but only returns *.go files
635 | // and filters out _test.go files since they're not relevant
636 | // and only slow things down.
637 | ctx.ReadDir = func(dir string) (notTests []os.FileInfo, err error) {
638 | all, err := ioutil.ReadDir(dir)
639 | if err != nil {
640 | return nil, err
641 | }
642 | notTests = all[:0]
643 | for _, fi := range all {
644 | name := fi.Name()
645 | if strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") {
646 | notTests = append(notTests, fi)
647 | }
648 | }
649 | return notTests, nil
650 | }
651 |
652 | files, err := ctx.ReadDir(dir)
653 | if err != nil {
654 | log.Print(err)
655 | return nil
656 | }
657 |
658 | fset := token.NewFileSet()
659 |
660 | for _, fi := range files {
661 | match, err := ctx.MatchFile(dir, fi.Name())
662 | if err != nil || !match {
663 | continue
664 | }
665 | fullFile := filepath.Join(dir, fi.Name())
666 | f, err := parser.ParseFile(fset, fullFile, nil, 0)
667 | if err != nil {
668 | if Debug {
669 | log.Printf("Parsing %s: %v", fullFile, err)
670 | }
671 | return nil
672 | }
673 | pkgName := f.Name.Name
674 | if pkgName == "documentation" {
675 | // Special case from go/build.ImportDir, not
676 | // handled by ctx.MatchFile.
677 | continue
678 | }
679 | if pkgName != expectPackage {
680 | if Debug {
681 | log.Printf("scan of dir %v is not expected package %v (actually %v)", dir, expectPackage, pkgName)
682 | }
683 | return nil
684 | }
685 | for name := range f.Scope.Objects {
686 | if ast.IsExported(name) {
687 | exports[name] = true
688 | }
689 | }
690 | }
691 |
692 | if Debug {
693 | exportList := make([]string, 0, len(exports))
694 | for k := range exports {
695 | exportList = append(exportList, k)
696 | }
697 | sort.Strings(exportList)
698 | log.Printf("loaded exports in dir %v (package %v): %v", dir, expectPackage, strings.Join(exportList, ", "))
699 | }
700 | return exports
701 | }
702 |
703 | // findImport searches for a package with the given symbols.
704 | // If no package is found, findImport returns ("", false, nil)
705 | //
706 | // This is declared as a variable rather than a function so goimports
707 | // can be easily extended by adding a file with an init function.
708 | //
709 | // The rename value tells goimports whether to use the package name as
710 | // a local qualifier in an import. For example, if findImports("pkg",
711 | // "X") returns ("foo/bar", rename=true), then goimports adds the
712 | // import line:
713 | // import pkg "foo/bar"
714 | // to satisfy uses of pkg.X in the file.
715 | var findImport func(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) = findImportGoPath
716 |
717 | // findImportGoPath is the normal implementation of findImport.
718 | // (Some companies have their own internally.)
719 | func findImportGoPath(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) {
720 | if inTests {
721 | testMu.RLock()
722 | defer testMu.RUnlock()
723 | }
724 |
725 | // Fast path for the standard library.
726 | // In the common case we hopefully never have to scan the GOPATH, which can
727 | // be slow with moving disks.
728 | if pkg, rename, ok := findImportStdlib(pkgName, symbols); ok {
729 | return pkg, rename, nil
730 | }
731 | if pkgName == "rand" && symbols["Read"] {
732 | // Special-case rand.Read.
733 | //
734 | // If findImportStdlib didn't find it above, don't go
735 | // searching for it, lest it find and pick math/rand
736 | // in GOROOT (new as of Go 1.6)
737 | //
738 | // crypto/rand is the safer choice.
739 | return "", false, nil
740 | }
741 |
742 | // TODO(sameer): look at the import lines for other Go files in the
743 | // local directory, since the user is likely to import the same packages
744 | // in the current Go file. Return rename=true when the other Go files
745 | // use a renamed package that's also used in the current file.
746 |
747 | // Read all the $GOPATH/src/.goimportsignore files before scanning directories.
748 | populateIgnoreOnce.Do(populateIgnore)
749 |
750 | // Start scanning the $GOROOT asynchronously, then run the
751 | // GOPATH scan synchronously if needed, and then wait for the
752 | // $GOROOT to finish.
753 | //
754 | // TODO(bradfitz): run each $GOPATH entry async. But nobody
755 | // really has more than one anyway, so low priority.
756 | scanGoRootOnce.Do(scanGoRoot) // async
757 | if !fileInDir(filename, build.Default.GOROOT) {
758 | scanGoPathOnce.Do(scanGoPath) // blocking
759 | }
760 | <-scanGoRootDone
761 |
762 | // Find candidate packages, looking only at their directory names first.
763 | var candidates []*pkg
764 | for _, pkg := range dirScan {
765 | if pkgIsCandidate(filename, pkgName, pkg) {
766 | candidates = append(candidates, pkg)
767 | }
768 | }
769 |
770 | // Sort the candidates by their import package length,
771 | // assuming that shorter package names are better than long
772 | // ones. Note that this sorts by the de-vendored name, so
773 | // there's no "penalty" for vendoring.
774 | sort.Sort(byImportPathShortLength(candidates))
775 | if Debug {
776 | for i, pkg := range candidates {
777 | log.Printf("%s candidate %d/%d: %v", pkgName, i+1, len(candidates), pkg.importPathShort)
778 | }
779 | }
780 |
781 | // Collect exports for packages with matching names.
782 |
783 | done := make(chan struct{}) // closed when we find the answer
784 | defer close(done)
785 |
786 | rescv := make([]chan *pkg, len(candidates))
787 | for i := range candidates {
788 | rescv[i] = make(chan *pkg)
789 | }
790 | const maxConcurrentPackageImport = 4
791 | loadExportsSem := make(chan struct{}, maxConcurrentPackageImport)
792 |
793 | go func() {
794 | for i, pkg := range candidates {
795 | select {
796 | case loadExportsSem <- struct{}{}:
797 | select {
798 | case <-done:
799 | return
800 | default:
801 | }
802 | case <-done:
803 | return
804 | }
805 | pkg := pkg
806 | resc := rescv[i]
807 | go func() {
808 | if inTests {
809 | testMu.RLock()
810 | defer testMu.RUnlock()
811 | }
812 | defer func() { <-loadExportsSem }()
813 | exports := loadExports(pkgName, pkg.dir)
814 |
815 | // If it doesn't have the right
816 | // symbols, send nil to mean no match.
817 | for symbol := range symbols {
818 | if !exports[symbol] {
819 | pkg = nil
820 | break
821 | }
822 | }
823 | select {
824 | case resc <- pkg:
825 | case <-done:
826 | }
827 | }()
828 | }
829 | }()
830 | for _, resc := range rescv {
831 | pkg := <-resc
832 | if pkg == nil {
833 | continue
834 | }
835 | // If the package name in the source doesn't match the import path's base,
836 | // return true so the rewriter adds a name (import foo "github.com/bar/go-foo")
837 | needsRename := path.Base(pkg.importPath) != pkgName
838 | return pkg.importPathShort, needsRename, nil
839 | }
840 | return "", false, nil
841 | }
842 |
843 | // pkgIsCandidate reports whether pkg is a candidate for satisfying the
844 | // finding which package pkgIdent in the file named by filename is trying
845 | // to refer to.
846 | //
847 | // This check is purely lexical and is meant to be as fast as possible
848 | // because it's run over all $GOPATH directories to filter out poor
849 | // candidates in order to limit the CPU and I/O later parsing the
850 | // exports in candidate packages.
851 | //
852 | // filename is the file being formatted.
853 | // pkgIdent is the package being searched for, like "client" (if
854 | // searching for "client.New")
855 | func pkgIsCandidate(filename, pkgIdent string, pkg *pkg) bool {
856 | // Check "internal" and "vendor" visibility:
857 | if !canUse(filename, pkg.dir) {
858 | return false
859 | }
860 |
861 | // Speed optimization to minimize disk I/O:
862 | // the last two components on disk must contain the
863 | // package name somewhere.
864 | //
865 | // This permits mismatch naming like directory
866 | // "go-foo" being package "foo", or "pkg.v3" being "pkg",
867 | // or directory "google.golang.org/api/cloudbilling/v1"
868 | // being package "cloudbilling", but doesn't
869 | // permit a directory "foo" to be package
870 | // "bar", which is strongly discouraged
871 | // anyway. There's no reason goimports needs
872 | // to be slow just to accomodate that.
873 | lastTwo := lastTwoComponents(pkg.importPathShort)
874 | if strings.Contains(lastTwo, pkgIdent) {
875 | return true
876 | }
877 | if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) {
878 | lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
879 | if strings.Contains(lastTwo, pkgIdent) {
880 | return true
881 | }
882 | }
883 |
884 | return false
885 | }
886 |
887 | func hasHyphenOrUpperASCII(s string) bool {
888 | for i := 0; i < len(s); i++ {
889 | b := s[i]
890 | if b == '-' || ('A' <= b && b <= 'Z') {
891 | return true
892 | }
893 | }
894 | return false
895 | }
896 |
897 | func lowerASCIIAndRemoveHyphen(s string) (ret string) {
898 | buf := make([]byte, 0, len(s))
899 | for i := 0; i < len(s); i++ {
900 | b := s[i]
901 | switch {
902 | case b == '-':
903 | continue
904 | case 'A' <= b && b <= 'Z':
905 | buf = append(buf, b+('a'-'A'))
906 | default:
907 | buf = append(buf, b)
908 | }
909 | }
910 | return string(buf)
911 | }
912 |
913 | // canUse reports whether the package in dir is usable from filename,
914 | // respecting the Go "internal" and "vendor" visibility rules.
915 | func canUse(filename, dir string) bool {
916 | // Fast path check, before any allocations. If it doesn't contain vendor
917 | // or internal, it's not tricky:
918 | // Note that this can false-negative on directories like "notinternal",
919 | // but we check it correctly below. This is just a fast path.
920 | if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") {
921 | return true
922 | }
923 |
924 | dirSlash := filepath.ToSlash(dir)
925 | if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") {
926 | return true
927 | }
928 | // Vendor or internal directory only visible from children of parent.
929 | // That means the path from the current directory to the target directory
930 | // can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal
931 | // or bar/vendor or bar/internal.
932 | // After stripping all the leading ../, the only okay place to see vendor or internal
933 | // is at the very beginning of the path.
934 | absfile, err := filepath.Abs(filename)
935 | if err != nil {
936 | return false
937 | }
938 | absdir, err := filepath.Abs(dir)
939 | if err != nil {
940 | return false
941 | }
942 | rel, err := filepath.Rel(absfile, absdir)
943 | if err != nil {
944 | return false
945 | }
946 | relSlash := filepath.ToSlash(rel)
947 | if i := strings.LastIndex(relSlash, "../"); i >= 0 {
948 | relSlash = relSlash[i+len("../"):]
949 | }
950 | return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal")
951 | }
952 |
953 | // lastTwoComponents returns at most the last two path components
954 | // of v, using either / or \ as the path separator.
955 | func lastTwoComponents(v string) string {
956 | nslash := 0
957 | for i := len(v) - 1; i >= 0; i-- {
958 | if v[i] == '/' || v[i] == '\\' {
959 | nslash++
960 | if nslash == 2 {
961 | return v[i:]
962 | }
963 | }
964 | }
965 | return v
966 | }
967 |
968 | type visitFn func(node ast.Node) ast.Visitor
969 |
970 | func (fn visitFn) Visit(node ast.Node) ast.Visitor {
971 | return fn(node)
972 | }
973 |
974 | func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, rename, ok bool) {
975 | for symbol := range symbols {
976 | key := shortPkg + "." + symbol
977 | path := stdlib[key]
978 | if path == "" {
979 | if key == "rand.Read" {
980 | continue
981 | }
982 | return "", false, false
983 | }
984 | if importPath != "" && importPath != path {
985 | // Ambiguous. Symbols pointed to different things.
986 | return "", false, false
987 | }
988 | importPath = path
989 | }
990 | if importPath == "" && shortPkg == "rand" && symbols["Read"] {
991 | return "crypto/rand", false, true
992 | }
993 | return importPath, false, importPath != ""
994 | }
995 |
996 | // fileInDir reports whether the provided file path looks like
997 | // it's in dir. (without hitting the filesystem)
998 | func fileInDir(file, dir string) bool {
999 | rest := strings.TrimPrefix(file, dir)
1000 | if len(rest) == len(file) {
1001 | // dir is not a prefix of file.
1002 | return false
1003 | }
1004 | // Check for boundary: either nothing (file == dir), or a slash.
1005 | return len(rest) == 0 || rest[0] == '/' || rest[0] == '\\'
1006 | }
1007 |
--------------------------------------------------------------------------------