├── .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 | --------------------------------------------------------------------------------