diff --git a/Makefile b/Makefile index 965b071f62..f73b0ed237 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Copyright 2015 The OPA Authors. All rights reserved. +# Copyright 2016 The OPA Authors. All rights reserved. # Use of this source code is governed by an Apache2 # license that can be found in the LICENSE file. @@ -18,9 +18,9 @@ GO := go GO15VENDOREXPERIMENT := 1 export GO15VENDOREXPERIMENT -.PHONY: all deps generate build test clean +.PHONY: all deps generate build test check check-fmt check-vet check-lint clean -all: build test +all: build test check deps: $(GO) install ./vendor/github.com/PuerkitoBio/pigeon @@ -35,5 +35,16 @@ build: generate test: generate $(GO) test -v $(PACKAGES) +check: check-fmt check-vet check-lint + +check-fmt: + ./build/check-fmt.sh + +check-vet: + ./build/check-vet.sh + +check-lint: + ./build/check-lint.sh + clean: rm -f ./opa diff --git a/build/check-fmt.sh b/build/check-fmt.sh new file mode 100755 index 0000000000..7c2eb312df --- /dev/null +++ b/build/check-fmt.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +OPA_DIR=$(dirname "${BASH_SOURCE}")/.. +source $OPA_DIR/build/utils.sh + +function opa::check_fmt() { + exec 5>&1 + exit_code=0 + for pkg in $(opa::go_packages); do + for file in $(opa::go_files_in_package $pkg); do + __diff=$(gofmt -d $file | tee >(cat - >&5)) + if [ ! -z "$__diff" ]; then + exit_code=1 + fi + done + done + exit $exit_code +} + +opa::check_fmt diff --git a/build/check-lint.sh b/build/check-lint.sh new file mode 100755 index 0000000000..c489651f22 --- /dev/null +++ b/build/check-lint.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +OPA_DIR=$( + dir=$(dirname "${BASH_SOURCE}")/.. + cd "$dir" + pwd +) +source $OPA_DIR/build/utils.sh + + +function opa::check_lint() { + exec 5>&1 + exit_code=0 + for pkg in $(opa::go_packages); do + echo $pkg + __output=$(golint $pkg | tee >(cat - >&5)) + if [ ! -z "$__output" ]; then + exit_code=1 + fi + done + exit $exit_code +} + +opa::check_lint diff --git a/build/check-vet.sh b/build/check-vet.sh new file mode 100755 index 0000000000..fb22408e58 --- /dev/null +++ b/build/check-vet.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +OPA_DIR=$( + dir=$(dirname "${BASH_SOURCE}")/.. + cd "$dir" + pwd +) +source $OPA_DIR/build/utils.sh + +function opa::check_vet() { + exec 5>&1 + rc=0 + exit_code=0 + for pkg in $(opa::go_packages); do + go vet $pkg || rc=$? + if [[ $rc != 0 ]]; then + exit_code=1 + fi + done + exit $exit_code +} + +opa::check_vet diff --git a/build/utils.sh b/build/utils.sh new file mode 100644 index 0000000000..b928cab9c9 --- /dev/null +++ b/build/utils.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -o errexit +set -o pipefail +set -o nounset + +function opa::go_packages() { + for pkg in $(go list ./.../ 2>/dev/null | grep -v vendor); do + echo $pkg + done +} + +function opa::go_files_in_package() { + dir=$(go list -f '{{ .Dir }}' $1) + for file in $(go list -f '{{ join .GoFiles "\n" }}' $1); do + echo $dir/$file + done + for file in $(go list -f '{{ join .TestGoFiles "\n" }}' $1); do + echo $dir/$file + done +} diff --git a/cmd/commands.go b/cmd/commands.go index f043c9048b..de38af617c 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -8,6 +8,7 @@ import "github.com/spf13/cobra" import "path" import "os" +// RootCommand is the base CLI command that all subcommands are added to. var RootCommand = &cobra.Command{ Use: path.Base(os.Args[0]), Short: "Open Policy Agent (OPA)", diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 735a7eb0b4..b6128577a8 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -18,14 +18,18 @@ Requirements: After cloning the repository, run `make deps` to install the parser generator ("pigeon") into your workspace. -Next, run `make all` to build the project and execute all of the tests. If -this succeeds, there should be a new binary in the top level directory ("opa"). +Next, run `make all` to build the project, execute all of the tests, and run +static analysis checks against the code. If this succeeds, there should be a +new binary in the top level directory ("opa"). Verify the build was successful by running `opa version`. You can re-build the project with `make build` and execute all of the tests with `make test`. +The static analysis checks (i.e., `go fmt`, `golint`, and `go vet` can be run +with `make check`). + ## Workflow 1. Go to [https://github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) and fork the repository @@ -57,7 +61,9 @@ with `make test`. 1. Develop your changes and regularly update your local branch against upstream. - - Make sure you run `go fmt` on your code before submitting a Pull Request. + - Be sure to run `make check` before submitting your pull request. You + may need to run `go fmt` on your code to make it comply with standard Go + style. 1. Commit changes and push to your fork. @@ -86,7 +92,7 @@ contained in the vendor directory. If you need to add a dependency to the project: -1. Run `glide get ` to download the package. +1. Run `glide get --update-vendored` to download the package. - This command should be used instead of `go get `. - The package will be stored under the vendor directory. - The glide.yaml file will be updated. @@ -102,4 +108,4 @@ If you need to update the dependencies: ## Opalog -If you need to modify the Opalog syntax you must update opalog/opalog.peg. Both `make build` and `make test` will re-generate the parser but if you want to test the parser generation explicitly you can run `make generate`. \ No newline at end of file +If you need to modify the Opalog syntax you must update opalog/opalog.peg. Both `make build` and `make test` will re-generate the parser but if you want to test the parser generation explicitly you can run `make generate`. diff --git a/glide.lock b/glide.lock index 8794622cb1..ed45d60d42 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: a0c5cde7a83a30f6cffbeaf27a08a4c566b0d740e24ab8bb3161f9315cd1d58e -updated: 2016-04-04T10:43:40.051156348-07:00 +hash: 3d658b2801a5a719294fa1eb719a2da3b63a220fbaa66d918c4fcfdc677c51aa +updated: 2016-04-05T09:24:23.839542269-07:00 imports: - name: github.com/armon/consul-api version: dcfedd50ed5334f96adee43fc88518a4f095e15c @@ -9,6 +9,8 @@ imports: version: 003851be7bb0694fe3cc457a49529a19388ee7cf - name: github.com/cpuguy83/go-md2man version: 2724a9c9051aa62e9cca11304e7dd518e9e41599 +- name: github.com/golang/lint + version: 8f348af5e29faa4262efdc14302797f23774e477 - name: github.com/hashicorp/hcl version: 2604f3bda7e8960c1be1063709e7d7f0765048d0 - name: github.com/kr/pretty @@ -44,7 +46,7 @@ imports: - name: golang.org/x/crypto version: 9e7f5dc375abeb9619ea3c5c58502c428f457aa2 - name: golang.org/x/net - version: 024ed629fd292398cfd43c9678a5bf004f7defdc + version: 318395d8b12f5dd0f1b7cd0fbb95195f49acb0f9 - name: golang.org/x/text version: 1b466db55e0ba5d56ef5315c728216b42f796491 - name: golang.org/x/tools diff --git a/glide.yaml b/glide.yaml index daa416d80d..e9bbfc019c 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,7 +1,8 @@ package: github.com/open-policy-agent/opa import: - - package: github.com/PuerkitoBio/pigeon - - package: golang.org/x/tools - subpackages: - - cmd/goimports - - package: github.com/spf13/cobra +- package: github.com/PuerkitoBio/pigeon +- package: golang.org/x/tools + subpackages: + - cmd/goimports +- package: github.com/spf13/cobra +- package: github.com/golang/lint diff --git a/opalog/parser_test.go b/opalog/parser_test.go index dae89d8737..25ca9efc3d 100644 --- a/opalog/parser_test.go +++ b/opalog/parser_test.go @@ -76,8 +76,8 @@ func TestArrayWithVars(t *testing.T) { } func TestEmptyComposites(t *testing.T) { - assertParseOneTerm(t, "empty object", "{}", ObjectTerm()) - assertParseOneTerm(t, "emtpy array", "[]", ArrayTerm()) + assertParseOneTerm(t, "empty object", "{}", ObjectTerm()) + assertParseOneTerm(t, "emtpy array", "[]", ArrayTerm()) } func TestNestedComposites(t *testing.T) { diff --git a/opalog/term.go b/opalog/term.go index 4f4a1a6f5e..e367259765 100644 --- a/opalog/term.go +++ b/opalog/term.go @@ -57,16 +57,20 @@ func (term *Term) String() string { return term.Value.String() } +// Null represents the null value defined by JSON. type Null struct{} +// NullTerm creates a new Term with a Null value. func NullTerm() *Term { return &Term{Value: Null{}} } +// NullTermWithLoc creates a new Term with a Null value and a Location. func NullTermWithLoc(loc *Location) *Term { return &Term{Value: Null{}, Location: loc} } +// Equal returns true if the other term Value is also Null. func (null Null) Equal(other Value) bool { switch other.(type) { case Null: @@ -80,16 +84,20 @@ func (null Null) String() string { return "null" } +// Boolean represents a boolean value defined by JSON. type Boolean bool +// BooleanTerm creates a new Term with a Boolean value. func BooleanTerm(b bool) *Term { return &Term{Value: Boolean(b)} } +// BooleanTermWithLoc creates a new Term with a Boolean value and a Location. func BooleanTermWithLoc(b bool, loc *Location) *Term { return &Term{Value: Boolean(b), Location: loc} } +// Equal returns true if the other Value is a Boolean and is equal. func (bol Boolean) Equal(other Value) bool { switch other := other.(type) { case Boolean: @@ -103,16 +111,20 @@ func (bol Boolean) String() string { return strconv.FormatBool(bool(bol)) } +// Number represents a numeric value as defined by JSON. type Number float64 +// NumberTerm creates a new Term with a Number value. func NumberTerm(n float64) *Term { return &Term{Value: Number(n)} } +// NumberTermWithLoc creates a new Term with a Number value and a Location. func NumberTermWithLoc(n float64, loc *Location) *Term { return &Term{Value: Number(n), Location: loc} } +// Equal returns true if the other Value is a Number and is equal. func (num Number) Equal(other Value) bool { switch other := other.(type) { case Number: @@ -126,16 +138,20 @@ func (num Number) String() string { return strconv.FormatFloat(float64(num), 'G', -1, 64) } +// String represents a string value as defined by JSON. type String string +// StringTerm creates a new Term with a String value. func StringTerm(s string) *Term { return &Term{Value: String(s)} } +// StringTermWithLoc creates a new Term with a String value and a Location. func StringTermWithLoc(s string, loc *Location) *Term { return &Term{Value: String(s), Location: loc} } +// Equal returns true if the other Value is a String and is equal. func (str String) Equal(other Value) bool { switch other := other.(type) { case String: @@ -149,16 +165,21 @@ func (str String) String() string { return strconv.Quote(string(str)) } +// Var represents a variable as defined by Opalog. type Var string +// VarTerm creates a new Term with a Variable value. func VarTerm(v string) *Term { return &Term{Value: Var(v)} } +// VarTermWithLoc creates a new Term with a Variable value and a Location. func VarTermWithLoc(v string, loc *Location) *Term { return &Term{Value: Var(v), Location: loc} } +// Equal returns true if the other Value is a Variable and has the same value +// (name). func (variable Var) Equal(other Value) bool { switch other := other.(type) { case Var: @@ -172,16 +193,21 @@ func (variable Var) String() string { return string(variable) } +// Ref represents a variable as defined by Opalog. type Ref []*Term +// RefTerm creates a new Term with a Ref value. func RefTerm(r ...*Term) *Term { return &Term{Value: Ref(r)} } +// RefTermWithLoc creates a new Term with a Ref value and a Location. func RefTermWithLoc(r []*Term, loc *Location) *Term { return &Term{Value: Ref(r), Location: loc} } +// Equal returns true if the other Value is a Ref and the elements of the +// other Ref are equal to the this Ref. func (ref Ref) Equal(other Value) bool { switch other := other.(type) { case Ref: @@ -217,16 +243,24 @@ func (ref Ref) String() string { return strings.Join(buf, "") } +// Array represents an array as defined by Opalog. Arrays are similar to the +// same types as defined by JSON with the exception that they can contain Vars +// and References. type Array []*Term +// ArrayTerm creates a new Term with an Array value. func ArrayTerm(a ...*Term) *Term { return &Term{Value: Array(a)} } +// ArrayTermWithLoc creates a new Term with an Array value and a Location. func ArrayTermWithLoc(a []*Term, loc *Location) *Term { return &Term{Value: Array(a), Location: loc} } +// Equal returns true if the other Value is an Array and the elements of the +// other Array are equal to the elements of this Array. The elements are +// ordered. func (arr Array) Equal(other Value) bool { switch other := other.(type) { case Array: @@ -250,20 +284,30 @@ func (arr Array) String() string { return "[" + strings.Join(buf, ", ") + "]" } +// Object represents an object as defined by Opalog. Objects are similar to +// the same types as defined by JSON with the exception that they can contain +// Vars and References. type Object [][2]*Term +// Item is a helper for constructing an tuple containing two Terms +// representing a key/value pair in an Object. func Item(key, value *Term) [2]*Term { return [2]*Term{key, value} } +// ObjectTerm creates a new Term with an Object value. func ObjectTerm(o ...[2]*Term) *Term { return &Term{Value: Object(o)} } +// ObjectTermWithLoc creates a new Term with an Object value and a Location. func ObjectTermWithLoc(o [][2]*Term, loc *Location) *Term { return &Term{Value: Object(o), Location: loc} } +// Equal returns true if the other Value is an Object and the key/value pairs +// of the Other object are equal to the key/value pairs of this Object. The +// key/value pairs are ordered. func (obj Object) Equal(other Value) bool { switch other := other.(type) { case Object: diff --git a/opalog/term_test.go b/opalog/term_test.go index 9b7d838711..79e338632b 100644 --- a/opalog/term_test.go +++ b/opalog/term_test.go @@ -11,8 +11,8 @@ func TestEqualTerms(t *testing.T) { assertTermEqual(t, BooleanTerm(true), BooleanTerm(true)) assertTermEqual(t, NumberTerm(5), NumberTerm(5)) assertTermEqual(t, StringTerm("a string"), StringTerm("a string")) - assertTermEqual(t, ObjectTerm(), ObjectTerm()) - assertTermEqual(t, ArrayTerm(), ArrayTerm()) + assertTermEqual(t, ObjectTerm(), ObjectTerm()) + assertTermEqual(t, ArrayTerm(), ArrayTerm()) assertTermEqual(t, ObjectTerm(Item(NumberTerm(1), NumberTerm(2))), ObjectTerm(Item(NumberTerm(1), NumberTerm(2)))) assertTermEqual(t, ObjectTerm(Item(NumberTerm(1), NumberTerm(2)), Item(NumberTerm(3), NumberTerm(4))), ObjectTerm(Item(NumberTerm(1), NumberTerm(2)), Item(NumberTerm(3), NumberTerm(4)))) assertTermEqual(t, ArrayTerm(NumberTerm(1), NumberTerm(2), NumberTerm(3)), ArrayTerm(NumberTerm(1), NumberTerm(2), NumberTerm(3))) @@ -46,14 +46,14 @@ func TestTermsToString(t *testing.T) { assertToString(t, RefTerm(VarTerm("foo"), StringTerm("bar")).Value, "foo.bar") assertToString(t, RefTerm(VarTerm("foo"), StringTerm("bar"), VarTerm("i"), NumberTerm(0), StringTerm("baz")).Value, "foo.bar[i][0].baz") assertToString(t, RefTerm(VarTerm("foo"), BooleanTerm(false), NullTerm(), StringTerm("bar")).Value, "foo[false][null].bar") - assertToString(t, ArrayTerm().Value, "[]") - assertToString(t, ObjectTerm().Value, "{}") + assertToString(t, ArrayTerm().Value, "[]") + assertToString(t, ObjectTerm().Value, "{}") assertToString(t, ArrayTerm(ObjectTerm(Item(VarTerm("foo"), ArrayTerm(RefTerm(VarTerm("bar"), VarTerm("i"))))), StringTerm("foo"), BooleanTerm(true), NullTerm(), NumberTerm(42.1)).Value, "[{foo: [bar[i]]}, \"foo\", true, null, 42.1]") } func assertToString(t *testing.T, val Value, expected string) { result := val.String() if result != expected { - t.Errorf("Expected %v for %f but got %v", expected, val, result) + t.Errorf("Expected %v but got %v", expected, result) } } diff --git a/vendor/github.com/golang/lint/.travis.yml b/vendor/github.com/golang/lint/.travis.yml new file mode 100644 index 0000000000..970855e41e --- /dev/null +++ b/vendor/github.com/golang/lint/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.5 + - 1.6 + +install: + - go get -t -v ./... + +script: + - go test -v ./... diff --git a/vendor/github.com/golang/lint/CONTRIBUTING.md b/vendor/github.com/golang/lint/CONTRIBUTING.md new file mode 100644 index 0000000000..971da126ec --- /dev/null +++ b/vendor/github.com/golang/lint/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# Contributing to Golint + +## Before filing an issue: + +### Are you having trouble building golint? + +Check you have the latest version of its dependencies. Run +``` +go get -u github.com/golang/lint +``` +If you still have problems, consider searching for existing issues before filing a new issue. + +## Before sending a pull request: + +Have you understood the purpose of golint? Make sure to carefully read `README`. diff --git a/vendor/github.com/golang/lint/LICENSE b/vendor/github.com/golang/lint/LICENSE new file mode 100644 index 0000000000..65d761bc9f --- /dev/null +++ b/vendor/github.com/golang/lint/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/golang/lint/README.md b/vendor/github.com/golang/lint/README.md new file mode 100644 index 0000000000..0b78a525d9 --- /dev/null +++ b/vendor/github.com/golang/lint/README.md @@ -0,0 +1,82 @@ +Golint is a linter for Go source code. + +[![Build Status](https://travis-ci.org/golang/lint.svg?branch=master)](https://travis-ci.org/golang/lint) + +## Installation + +Golint requires Go 1.5 or later. + + go get -u github.com/golang/lint/golint + +## Usage + +Invoke `golint` with one or more filenames, a directory, or a package named +by its import path. Golint uses the same +[import path syntax](https://golang.org/cmd/go/#hdr-Import_path_syntax) as +the `go` command and therefore +also supports relative import paths like `./...`. Additionally the `...` +wildcard can be used as suffix on relative and absolute file paths to recurse +into them. + +The output of this tool is a list of suggestions in Vim quickfix format, +which is accepted by lots of different editors. + +## Purpose + +Golint differs from gofmt. Gofmt reformats Go source code, whereas +golint prints out style mistakes. + +Golint differs from govet. Govet is concerned with correctness, whereas +golint is concerned with coding style. Golint is in use at Google, and it +seeks to match the accepted style of the open source Go project. + +The suggestions made by golint are exactly that: suggestions. +Golint is not perfect, and has both false positives and false negatives. +Do not treat its output as a gold standard. We will not be adding pragmas +or other knobs to suppress specific warnings, so do not expect or require +code to be completely "lint-free". +In short, this tool is not, and will never be, trustworthy enough for its +suggestions to be enforced automatically, for example as part of a build process. +Golint makes suggestions for many of the mechanically checkable items listed in +[Effective Go](https://golang.org/doc/effective_go.html) and the +[CodeReviewComments wiki page](https://golang.org/wiki/CodeReviewComments). + +If you find an established style that is frequently violated, and which +you think golint could statically check, +[file an issue](https://github.com/golang/lint/issues). + +## Contributions + +Contributions to this project are welcome, though please send mail before +starting work on anything major. Contributors retain their copyright, so we +need you to fill out +[a short form](https://developers.google.com/open-source/cla/individual) +before we can accept your contribution. + +## Vim + +Add this to your ~/.vimrc: + + set rtp+=$GOPATH/src/github.com/golang/lint/misc/vim + +If you have multiple entries in your GOPATH, replace `$GOPATH` with the right value. + +Running `:Lint` will run golint on the current file and populate the quickfix list. + +Optionally, add this to your `~/.vimrc` to automatically run `golint` on `:w` + + autocmd BufWritePost,FileWritePost *.go execute 'Lint' | cwindow + + +## Emacs + +Add this to your `.emacs` file: + + (add-to-list 'load-path (concat (getenv "GOPATH") "/src/github.com/golang/lint/misc/emacs")) + (require 'golint) + +If you have multiple entries in your GOPATH, replace `$GOPATH` with the right value. + +Running M-x golint will run golint on the current file. + +For more usage, see [Compilation-Mode](http://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html). diff --git a/vendor/github.com/golang/lint/golint/golint.go b/vendor/github.com/golang/lint/golint/golint.go new file mode 100644 index 0000000000..eb39199efa --- /dev/null +++ b/vendor/github.com/golang/lint/golint/golint.go @@ -0,0 +1,127 @@ +// Copyright (c) 2013 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd. + +// golint lints the Go source files named on its command line. +package main + +import ( + "flag" + "fmt" + "go/build" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/golang/lint" +) + +var minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it") + +func usage() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n") + fmt.Fprintf(os.Stderr, "\tgolint [flags] package\n") + fmt.Fprintf(os.Stderr, "\tgolint [flags] directory\n") + fmt.Fprintf(os.Stderr, "\tgolint [flags] files... # must be a single package\n") + fmt.Fprintf(os.Stderr, "Flags:\n") + flag.PrintDefaults() +} + +func main() { + flag.Usage = usage + flag.Parse() + + switch flag.NArg() { + case 0: + lintDir(".") + case 1: + arg := flag.Arg(0) + if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-4]) { + for _, dirname := range allPackagesInFS(arg) { + lintDir(dirname) + } + } else if isDir(arg) { + lintDir(arg) + } else if exists(arg) { + lintFiles(arg) + } else { + for _, pkgname := range importPaths([]string{arg}) { + lintPackage(pkgname) + } + } + default: + lintFiles(flag.Args()...) + } +} + +func isDir(filename string) bool { + fi, err := os.Stat(filename) + return err == nil && fi.IsDir() +} + +func exists(filename string) bool { + _, err := os.Stat(filename) + return err == nil +} + +func lintFiles(filenames ...string) { + files := make(map[string][]byte) + for _, filename := range filenames { + src, err := ioutil.ReadFile(filename) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + files[filename] = src + } + + l := new(lint.Linter) + ps, err := l.LintFiles(files) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return + } + for _, p := range ps { + if p.Confidence >= *minConfidence { + fmt.Printf("%v: %s\n", p.Position, p.Text) + } + } +} + +func lintDir(dirname string) { + pkg, err := build.ImportDir(dirname, 0) + lintImportedPackage(pkg, err) +} + +func lintPackage(pkgname string) { + pkg, err := build.Import(pkgname, ".", 0) + lintImportedPackage(pkg, err) +} + +func lintImportedPackage(pkg *build.Package, err error) { + if err != nil { + if _, nogo := err.(*build.NoGoError); nogo { + // Don't complain if the failure is due to no Go source files. + return + } + fmt.Fprintln(os.Stderr, err) + return + } + + var files []string + files = append(files, pkg.GoFiles...) + files = append(files, pkg.CgoFiles...) + files = append(files, pkg.TestGoFiles...) + if pkg.Dir != "." { + for i, f := range files { + files[i] = filepath.Join(pkg.Dir, f) + } + } + // TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles) + + lintFiles(files...) +} diff --git a/vendor/github.com/golang/lint/golint/import.go b/vendor/github.com/golang/lint/golint/import.go new file mode 100644 index 0000000000..02a0daa61a --- /dev/null +++ b/vendor/github.com/golang/lint/golint/import.go @@ -0,0 +1,310 @@ +package main + +/* + +This file holds a direct copy of the import path matching code of +https://github.com/golang/go/blob/master/src/cmd/go/main.go. It can be +replaced when https://golang.org/issue/8768 is resolved. + +It has been updated to follow upstream changes in a few ways. + +*/ + +import ( + "fmt" + "go/build" + "log" + "os" + "path" + "path/filepath" + "regexp" + "runtime" + "strings" +) + +var buildContext = build.Default + +var ( + goroot = filepath.Clean(runtime.GOROOT()) + gorootSrc = filepath.Join(goroot, "src") +) + +// importPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +func importPathsNoDotExpansion(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + path.Clean(a) + if a == "./." { + a = "." + } + } else { + a = path.Clean(a) + } + if a == "all" || a == "std" { + out = append(out, allPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +// importPaths returns the import paths to use for the given command line. +func importPaths(args []string) []string { + args = importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, allPackagesInFS(a)...) + } else { + out = append(out, allPackages(a)...) + } + continue + } + out = append(out, a) + } + return out +} + +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +func matchPattern(pattern string) func(name string) bool { + re := regexp.QuoteMeta(pattern) + re = strings.Replace(re, `\.\.\.`, `.*`, -1) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } + reg := regexp.MustCompile(`^` + re + `$`) + return func(name string) bool { + return reg.MatchString(name) + } +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + +// allPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages) +// or a path including "...". +func allPackages(pattern string) []string { + pkgs := matchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackages(pattern string) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if pattern != "all" && pattern != "std" { + match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !buildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + // Commands + cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) + filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == cmd { + return nil + } + name := path[len(cmd):] + if !treeCanMatch(name) { + return filepath.SkipDir + } + // Commands are all in cmd/, not in subdirectories. + if strings.Contains(name, string(filepath.Separator)) { + return filepath.SkipDir + } + + // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. + name = "cmd/" + name + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + + for _, src := range buildContext.SrcDirs() { + if (pattern == "std" || pattern == "cmd") && src != gorootSrc { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + root := src + if pattern == "cmd" { + root += "cmd" + string(filepath.Separator) + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == src { + return nil + } + + // Avoid .foo, _foo, and testdata directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { + // The name "std" is only the standard library. + // If the name is cmd, it's the root of the command tree. + return filepath.SkipDir + } + if !treeCanMatch(name) { + return filepath.SkipDir + } + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } + } + pkgs = append(pkgs, name) + return nil + }) + } + return pkgs +} + +// allPackagesInFS is like allPackages but is passed a pattern +// beginning ./ or ../, meaning it should scan the tree rooted +// at the given directory. There are ... in the pattern too. +func allPackagesInFS(pattern string) []string { + pkgs := matchPackagesInFS(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackagesInFS(pattern string) []string { + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := path.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := matchPattern(pattern) + + var pkgs []string + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + if path == dir { + // filepath.Walk starts at dir and recurses. For the recursive case, + // the path is the result of filepath.Join, which calls filepath.Clean. + // The initial case is not Cleaned, though, so we do this explicitly. + // + // This converts a path like "./io/" to "io". Without this step, running + // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io + // package, because prepending the prefix "./" to the unclean path would + // result in "././io", and match("././io") returns false. + path = filepath.Clean(path) + } + + // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". + _, elem := filepath.Split(path) + dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." + if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { + return filepath.SkipDir + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + if _, err = build.ImportDir(path, 0); err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + return pkgs +} diff --git a/vendor/github.com/golang/lint/lint.go b/vendor/github.com/golang/lint/lint.go new file mode 100644 index 0000000000..e84f46daf1 --- /dev/null +++ b/vendor/github.com/golang/lint/lint.go @@ -0,0 +1,1562 @@ +// Copyright (c) 2013 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd. + +// Package lint contains a linter for Go source code. +package lint + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "go/types" + "regexp" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/tools/go/gcimporter15" +) + +const styleGuideBase = "https://golang.org/wiki/CodeReviewComments" + +// A Linter lints Go source code. +type Linter struct { +} + +// Problem represents a problem in some source code. +type Problem struct { + Position token.Position // position in source file + Text string // the prose that describes the problem + Link string // (optional) the link to the style guide for the problem + Confidence float64 // a value in (0,1] estimating the confidence in this problem's correctness + LineText string // the source line + Category string // a short name for the general category of the problem + + // If the problem has a suggested fix (the minority case), + // ReplacementLine is a full replacement for the relevant line of the source file. + ReplacementLine string +} + +func (p *Problem) String() string { + if p.Link != "" { + return p.Text + "\n\n" + p.Link + } + return p.Text +} + +type byPosition []Problem + +func (p byPosition) Len() int { return len(p) } +func (p byPosition) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +func (p byPosition) Less(i, j int) bool { + pi, pj := p[i].Position, p[j].Position + + if pi.Filename != pj.Filename { + return pi.Filename < pj.Filename + } + if pi.Line != pj.Line { + return pi.Line < pj.Line + } + if pi.Column != pj.Column { + return pi.Column < pj.Column + } + + return p[i].Text < p[j].Text +} + +// Lint lints src. +func (l *Linter) Lint(filename string, src []byte) ([]Problem, error) { + return l.LintFiles(map[string][]byte{filename: src}) +} + +// LintFiles lints a set of files of a single package. +// The argument is a map of filename to source. +func (l *Linter) LintFiles(files map[string][]byte) ([]Problem, error) { + if len(files) == 0 { + return nil, nil + } + pkg := &pkg{ + fset: token.NewFileSet(), + files: make(map[string]*file), + } + var pkgName string + for filename, src := range files { + f, err := parser.ParseFile(pkg.fset, filename, src, parser.ParseComments) + if err != nil { + return nil, err + } + if pkgName == "" { + pkgName = f.Name.Name + } else if f.Name.Name != pkgName { + return nil, fmt.Errorf("%s is in package %s, not %s", filename, f.Name.Name, pkgName) + } + pkg.files[filename] = &file{ + pkg: pkg, + f: f, + fset: pkg.fset, + src: src, + filename: filename, + } + } + return pkg.lint(), nil +} + +// pkg represents a package being linted. +type pkg struct { + fset *token.FileSet + files map[string]*file + + typesPkg *types.Package + typesInfo *types.Info + + // sortable is the set of types in the package that implement sort.Interface. + sortable map[string]bool + // main is whether this is a "main" package. + main bool + + problems []Problem +} + +func (p *pkg) lint() []Problem { + if err := p.typeCheck(); err != nil { + /* TODO(dsymonds): Consider reporting these errors when golint operates on entire packages. + if e, ok := err.(types.Error); ok { + pos := p.fset.Position(e.Pos) + conf := 1.0 + if strings.Contains(e.Msg, "can't find import: ") { + // Golint is probably being run in a context that doesn't support + // typechecking (e.g. package files aren't found), so don't warn about it. + conf = 0 + } + if conf > 0 { + p.errorfAt(pos, conf, category("typechecking"), e.Msg) + } + + // TODO(dsymonds): Abort if !e.Soft? + } + */ + } + + p.scanSortable() + p.main = p.isMain() + + for _, f := range p.files { + f.lint() + } + + sort.Sort(byPosition(p.problems)) + + return p.problems +} + +// file represents a file being linted. +type file struct { + pkg *pkg + f *ast.File + fset *token.FileSet + src []byte + filename string +} + +func (f *file) isTest() bool { return strings.HasSuffix(f.filename, "_test.go") } + +func (f *file) lint() { + f.lintPackageComment() + f.lintImports() + f.lintBlankImports() + f.lintExported() + f.lintNames() + f.lintVarDecls() + f.lintElses() + f.lintRanges() + f.lintErrorf() + f.lintErrors() + f.lintErrorStrings() + f.lintReceiverNames() + f.lintIncDec() + f.lintMake() + f.lintErrorReturn() + f.lintUnexportedReturn() + f.lintTimeNames() +} + +type link string +type category string + +// The variadic arguments may start with link and category types, +// and must end with a format string and any arguments. +// It returns the new Problem. +func (f *file) errorf(n ast.Node, confidence float64, args ...interface{}) *Problem { + pos := f.fset.Position(n.Pos()) + if pos.Filename == "" { + pos.Filename = f.filename + } + return f.pkg.errorfAt(pos, confidence, args...) +} + +func (p *pkg) errorfAt(pos token.Position, confidence float64, args ...interface{}) *Problem { + problem := Problem{ + Position: pos, + Confidence: confidence, + } + if pos.Filename != "" { + // The file might not exist in our mapping if a //line directive was encountered. + if f, ok := p.files[pos.Filename]; ok { + problem.LineText = srcLine(f.src, pos) + } + } + +argLoop: + for len(args) > 1 { // always leave at least the format string in args + switch v := args[0].(type) { + case link: + problem.Link = string(v) + case category: + problem.Category = string(v) + default: + break argLoop + } + args = args[1:] + } + + problem.Text = fmt.Sprintf(args[0].(string), args[1:]...) + + p.problems = append(p.problems, problem) + return &p.problems[len(p.problems)-1] +} + +var gcImporter = gcimporter.Import + +// importer implements go/types.Importer. +// It also implements go/types.ImporterFrom, which was new in Go 1.6, +// so vendoring will work. +type importer struct { + impFn func(packages map[string]*types.Package, path, srcDir string) (*types.Package, error) + packages map[string]*types.Package +} + +func (i importer) Import(path string) (*types.Package, error) { + return i.impFn(i.packages, path, "") +} + +// (importer).ImportFrom is in lint16.go. + +func (p *pkg) typeCheck() error { + config := &types.Config{ + // By setting a no-op error reporter, the type checker does as much work as possible. + Error: func(error) {}, + Importer: importer{ + impFn: gcImporter, + packages: make(map[string]*types.Package), + }, + } + info := &types.Info{ + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Scopes: make(map[ast.Node]*types.Scope), + } + var anyFile *file + var astFiles []*ast.File + for _, f := range p.files { + anyFile = f + astFiles = append(astFiles, f.f) + } + pkg, err := config.Check(anyFile.f.Name.Name, p.fset, astFiles, info) + // Remember the typechecking info, even if config.Check failed, + // since we will get partial information. + p.typesPkg = pkg + p.typesInfo = info + return err +} + +func (p *pkg) typeOf(expr ast.Expr) types.Type { + if p.typesInfo == nil { + return nil + } + return p.typesInfo.TypeOf(expr) +} + +func (p *pkg) isNamedType(typ types.Type, importPath, name string) bool { + n, ok := typ.(*types.Named) + if !ok { + return false + } + tn := n.Obj() + return tn != nil && tn.Pkg() != nil && tn.Pkg().Path() == importPath && tn.Name() == name +} + +// scopeOf returns the tightest scope encompassing id. +func (p *pkg) scopeOf(id *ast.Ident) *types.Scope { + var scope *types.Scope + if obj := p.typesInfo.ObjectOf(id); obj != nil { + scope = obj.Parent() + } + if scope == p.typesPkg.Scope() { + // We were given a top-level identifier. + // Use the file-level scope instead of the package-level scope. + pos := id.Pos() + for _, f := range p.files { + if f.f.Pos() <= pos && pos < f.f.End() { + scope = p.typesInfo.Scopes[f.f] + break + } + } + } + return scope +} + +func (p *pkg) scanSortable() { + p.sortable = make(map[string]bool) + + // bitfield for which methods exist on each type. + const ( + Len = 1 << iota + Less + Swap + ) + nmap := map[string]int{"Len": Len, "Less": Less, "Swap": Swap} + has := make(map[string]int) + for _, f := range p.files { + f.walk(func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 { + return true + } + // TODO(dsymonds): We could check the signature to be more precise. + recv := receiverType(fn) + if i, ok := nmap[fn.Name.Name]; ok { + has[recv] |= i + } + return false + }) + } + for typ, ms := range has { + if ms == Len|Less|Swap { + p.sortable[typ] = true + } + } +} + +func (p *pkg) isMain() bool { + for _, f := range p.files { + if f.isMain() { + return true + } + } + return false +} + +func (f *file) isMain() bool { + if f.f.Name.Name == "main" { + return true + } + return false +} + +// lintPackageComment checks package comments. It complains if +// there is no package comment, or if it is not of the right form. +// This has a notable false positive in that a package comment +// could rightfully appear in a different file of the same package, +// but that's not easy to fix since this linter is file-oriented. +func (f *file) lintPackageComment() { + if f.isTest() { + return + } + + const ref = styleGuideBase + "#package-comments" + prefix := "Package " + f.f.Name.Name + " " + + // Look for a detached package comment. + // First, scan for the last comment that occurs before the "package" keyword. + var lastCG *ast.CommentGroup + for _, cg := range f.f.Comments { + if cg.Pos() > f.f.Package { + // Gone past "package" keyword. + break + } + lastCG = cg + } + if lastCG != nil && strings.HasPrefix(lastCG.Text(), prefix) { + endPos := f.fset.Position(lastCG.End()) + pkgPos := f.fset.Position(f.f.Package) + if endPos.Line+1 < pkgPos.Line { + // There isn't a great place to anchor this error; + // the start of the blank lines between the doc and the package statement + // is at least pointing at the location of the problem. + pos := token.Position{ + Filename: endPos.Filename, + // Offset not set; it is non-trivial, and doesn't appear to be needed. + Line: endPos.Line + 1, + Column: 1, + } + f.pkg.errorfAt(pos, 0.9, link(ref), category("comments"), "package comment is detached; there should be no blank lines between it and the package statement") + return + } + } + + if f.f.Doc == nil { + f.errorf(f.f, 0.2, link(ref), category("comments"), "should have a package comment, unless it's in another file for this package") + return + } + s := f.f.Doc.Text() + if ts := strings.TrimLeft(s, " \t"); ts != s { + f.errorf(f.f.Doc, 1, link(ref), category("comments"), "package comment should not have leading space") + s = ts + } + // Only non-main packages need to keep to this form. + if !f.pkg.main && !strings.HasPrefix(s, prefix) { + f.errorf(f.f.Doc, 1, link(ref), category("comments"), `package comment should be of the form "%s..."`, prefix) + } +} + +// lintBlankImports complains if a non-main package has blank imports that are +// not documented. +func (f *file) lintBlankImports() { + // In package main and in tests, we don't complain about blank imports. + if f.pkg.main || f.isTest() { + return + } + + // The first element of each contiguous group of blank imports should have + // an explanatory comment of some kind. + for i, imp := range f.f.Imports { + pos := f.fset.Position(imp.Pos()) + + if !isBlank(imp.Name) { + continue // Ignore non-blank imports. + } + if i > 0 { + prev := f.f.Imports[i-1] + prevPos := f.fset.Position(prev.Pos()) + if isBlank(prev.Name) && prevPos.Line+1 == pos.Line { + continue // A subsequent blank in a group. + } + } + + // This is the first blank import of a group. + if imp.Doc == nil && imp.Comment == nil { + ref := "" + f.errorf(imp, 1, link(ref), category("imports"), "a blank import should be only in a main or test package, or have a comment justifying it") + } + } +} + +// lintImports examines import blocks. +func (f *file) lintImports() { + + for i, is := range f.f.Imports { + _ = i + if is.Name != nil && is.Name.Name == "." && !f.isTest() { + f.errorf(is, 1, link(styleGuideBase+"#import-dot"), category("imports"), "should not use dot imports") + } + + } + +} + +const docCommentsLink = styleGuideBase + "#doc-comments" + +// lintExported examines the exported names. +// It complains if any required doc comments are missing, +// or if they are not of the right form. The exact rules are in +// lintFuncDoc, lintTypeDoc and lintValueSpecDoc; this function +// also tracks the GenDecl structure being traversed to permit +// doc comments for constants to be on top of the const block. +// It also complains if the names stutter when combined with +// the package name. +func (f *file) lintExported() { + if f.isTest() { + return + } + + var lastGen *ast.GenDecl // last GenDecl entered. + + // Set of GenDecls that have already had missing comments flagged. + genDeclMissingComments := make(map[*ast.GenDecl]bool) + + f.walk(func(node ast.Node) bool { + switch v := node.(type) { + case *ast.GenDecl: + if v.Tok == token.IMPORT { + return false + } + // token.CONST, token.TYPE or token.VAR + lastGen = v + return true + case *ast.FuncDecl: + f.lintFuncDoc(v) + if v.Recv == nil { + // Only check for stutter on functions, not methods. + // Method names are not used package-qualified. + f.checkStutter(v.Name, "func") + } + // Don't proceed inside funcs. + return false + case *ast.TypeSpec: + // inside a GenDecl, which usually has the doc + doc := v.Doc + if doc == nil { + doc = lastGen.Doc + } + f.lintTypeDoc(v, doc) + f.checkStutter(v.Name, "type") + // Don't proceed inside types. + return false + case *ast.ValueSpec: + f.lintValueSpecDoc(v, lastGen, genDeclMissingComments) + return false + } + return true + }) +} + +var allCapsRE = regexp.MustCompile(`^[A-Z0-9_]+$`) + +// lintNames examines all names in the file. +// It complains if any use underscores or incorrect known initialisms. +func (f *file) lintNames() { + // Package names need slightly different handling than other names. + if strings.Contains(f.f.Name.Name, "_") && !strings.HasSuffix(f.f.Name.Name, "_test") { + f.errorf(f.f, 1, link("http://golang.org/doc/effective_go.html#package-names"), category("naming"), "don't use an underscore in package name") + } + + check := func(id *ast.Ident, thing string) { + if id.Name == "_" { + return + } + + // Handle two common styles from other languages that don't belong in Go. + if len(id.Name) >= 5 && allCapsRE.MatchString(id.Name) && strings.Contains(id.Name, "_") { + f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use ALL_CAPS in Go names; use CamelCase") + return + } + if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' { + should := string(id.Name[1]+'a'-'A') + id.Name[2:] + f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use leading k in Go names; %s %s should be %s", thing, id.Name, should) + } + + should := lintName(id.Name) + if id.Name == should { + return + } + if len(id.Name) > 2 && strings.Contains(id.Name[1:], "_") { + f.errorf(id, 0.9, link("http://golang.org/doc/effective_go.html#mixed-caps"), category("naming"), "don't use underscores in Go names; %s %s should be %s", thing, id.Name, should) + return + } + f.errorf(id, 0.8, link(styleGuideBase+"#initialisms"), category("naming"), "%s %s should be %s", thing, id.Name, should) + } + checkList := func(fl *ast.FieldList, thing string) { + if fl == nil { + return + } + for _, f := range fl.List { + for _, id := range f.Names { + check(id, thing) + } + } + } + f.walk(func(node ast.Node) bool { + switch v := node.(type) { + case *ast.AssignStmt: + if v.Tok == token.ASSIGN { + return true + } + for _, exp := range v.Lhs { + if id, ok := exp.(*ast.Ident); ok { + check(id, "var") + } + } + case *ast.FuncDecl: + if f.isTest() && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) { + return true + } + + thing := "func" + if v.Recv != nil { + thing = "method" + } + + check(v.Name, thing) + + checkList(v.Type.Params, thing+" parameter") + checkList(v.Type.Results, thing+" result") + case *ast.GenDecl: + if v.Tok == token.IMPORT { + return true + } + var thing string + switch v.Tok { + case token.CONST: + thing = "const" + case token.TYPE: + thing = "type" + case token.VAR: + thing = "var" + } + for _, spec := range v.Specs { + switch s := spec.(type) { + case *ast.TypeSpec: + check(s.Name, thing) + case *ast.ValueSpec: + for _, id := range s.Names { + check(id, thing) + } + } + } + case *ast.InterfaceType: + // Do not check interface method names. + // They are often constrainted by the method names of concrete types. + for _, x := range v.Methods.List { + ft, ok := x.Type.(*ast.FuncType) + if !ok { // might be an embedded interface name + continue + } + checkList(ft.Params, "interface method parameter") + checkList(ft.Results, "interface method result") + } + case *ast.RangeStmt: + if v.Tok == token.ASSIGN { + return true + } + if id, ok := v.Key.(*ast.Ident); ok { + check(id, "range var") + } + if id, ok := v.Value.(*ast.Ident); ok { + check(id, "range var") + } + case *ast.StructType: + for _, f := range v.Fields.List { + for _, id := range f.Names { + check(id, "struct field") + } + } + } + return true + }) +} + +// lintName returns a different name if it should be different. +func lintName(name string) (should string) { + // Fast path for simple cases: "_" and all lowercase. + if name == "_" { + return name + } + allLower := true + for _, r := range name { + if !unicode.IsLower(r) { + allLower = false + break + } + } + if allLower { + return name + } + + // Split camelCase at any lower->upper transition, and split on underscores. + // Check each word for common initialisms. + runes := []rune(name) + w, i := 0, 0 // index of start of word, scan + for i+1 <= len(runes) { + eow := false // whether we hit the end of a word + if i+1 == len(runes) { + eow = true + } else if runes[i+1] == '_' { + // underscore; shift the remainder forward over any run of underscores + eow = true + n := 1 + for i+n+1 < len(runes) && runes[i+n+1] == '_' { + n++ + } + + // Leave at most one underscore if the underscore is between two digits + if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) { + n-- + } + + copy(runes[i+1:], runes[i+n+1:]) + runes = runes[:len(runes)-n] + } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) { + // lower->non-lower + eow = true + } + i++ + if !eow { + continue + } + + // [w,i) is a word. + word := string(runes[w:i]) + if u := strings.ToUpper(word); commonInitialisms[u] { + // Keep consistent case, which is lowercase only at the start. + if w == 0 && unicode.IsLower(runes[w]) { + u = strings.ToLower(u) + } + // All the common initialisms are ASCII, + // so we can replace the bytes exactly. + copy(runes[w:], []rune(u)) + } else if w > 0 && strings.ToLower(word) == word { + // already all lowercase, and not the first word, so uppercase the first character. + runes[w] = unicode.ToUpper(runes[w]) + } + w = i + } + return string(runes) +} + +// commonInitialisms is a set of common initialisms. +// Only add entries that are highly unlikely to be non-initialisms. +// For instance, "ID" is fine (Freudian code is rare), but "AND" is not. +var commonInitialisms = map[string]bool{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SQL": true, + "SSH": true, + "TCP": true, + "TLS": true, + "TTL": true, + "UDP": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, +} + +// lintTypeDoc examines the doc comment on a type. +// It complains if they are missing from an exported type, +// or if they are not of the standard form. +func (f *file) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup) { + if !ast.IsExported(t.Name.Name) { + return + } + if doc == nil { + f.errorf(t, 1, link(docCommentsLink), category("comments"), "exported type %v should have comment or be unexported", t.Name) + return + } + + s := doc.Text() + articles := [...]string{"A", "An", "The"} + for _, a := range articles { + if strings.HasPrefix(s, a+" ") { + s = s[len(a)+1:] + break + } + } + if !strings.HasPrefix(s, t.Name.Name+" ") { + f.errorf(doc, 1, link(docCommentsLink), category("comments"), `comment on exported type %v should be of the form "%v ..." (with optional leading article)`, t.Name, t.Name) + } +} + +var commonMethods = map[string]bool{ + "Error": true, + "Read": true, + "ServeHTTP": true, + "String": true, + "Write": true, +} + +// lintFuncDoc examines doc comments on functions and methods. +// It complains if they are missing, or not of the right form. +// It has specific exclusions for well-known methods (see commonMethods above). +func (f *file) lintFuncDoc(fn *ast.FuncDecl) { + if !ast.IsExported(fn.Name.Name) { + // func is unexported + return + } + kind := "function" + name := fn.Name.Name + if fn.Recv != nil && len(fn.Recv.List) > 0 { + // method + kind = "method" + recv := receiverType(fn) + if !ast.IsExported(recv) { + // receiver is unexported + return + } + if commonMethods[name] { + return + } + switch name { + case "Len", "Less", "Swap": + if f.pkg.sortable[recv] { + return + } + } + name = recv + "." + name + } + if fn.Doc == nil { + f.errorf(fn, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment or be unexported", kind, name) + return + } + s := fn.Doc.Text() + prefix := fn.Name.Name + " " + if !strings.HasPrefix(s, prefix) { + f.errorf(fn.Doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix) + } +} + +// lintValueSpecDoc examines package-global variables and constants. +// It complains if they are not individually declared, +// or if they are not suitably documented in the right form (unless they are in a block that is commented). +func (f *file) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genDeclMissingComments map[*ast.GenDecl]bool) { + kind := "var" + if gd.Tok == token.CONST { + kind = "const" + } + + if len(vs.Names) > 1 { + // Check that none are exported except for the first. + for _, n := range vs.Names[1:] { + if ast.IsExported(n.Name) { + f.errorf(vs, 1, category("comments"), "exported %s %s should have its own declaration", kind, n.Name) + return + } + } + } + + // Only one name. + name := vs.Names[0].Name + if !ast.IsExported(name) { + return + } + + if vs.Doc == nil && gd.Doc == nil { + if genDeclMissingComments[gd] { + return + } + block := "" + if kind == "const" && gd.Lparen.IsValid() { + block = " (or a comment on this block)" + } + f.errorf(vs, 1, link(docCommentsLink), category("comments"), "exported %s %s should have comment%s or be unexported", kind, name, block) + genDeclMissingComments[gd] = true + return + } + // If this GenDecl has parens and a comment, we don't check its comment form. + if gd.Lparen.IsValid() && gd.Doc != nil { + return + } + // The relevant text to check will be on either vs.Doc or gd.Doc. + // Use vs.Doc preferentially. + doc := vs.Doc + if doc == nil { + doc = gd.Doc + } + prefix := name + " " + if !strings.HasPrefix(doc.Text(), prefix) { + f.errorf(doc, 1, link(docCommentsLink), category("comments"), `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix) + } +} + +func (f *file) checkStutter(id *ast.Ident, thing string) { + pkg, name := f.f.Name.Name, id.Name + if !ast.IsExported(name) { + // unexported name + return + } + // A name stutters if the package name is a strict prefix + // and the next character of the name starts a new word. + if len(name) <= len(pkg) { + // name is too short to stutter. + // This permits the name to be the same as the package name. + return + } + if !strings.EqualFold(pkg, name[:len(pkg)]) { + return + } + // We can assume the name is well-formed UTF-8. + // If the next rune after the package name is uppercase or an underscore + // the it's starting a new word and thus this name stutters. + rem := name[len(pkg):] + if next, _ := utf8.DecodeRuneInString(rem); next == '_' || unicode.IsUpper(next) { + f.errorf(id, 0.8, link(styleGuideBase+"#package-names"), category("naming"), "%s name will be used as %s.%s by other packages, and that stutters; consider calling this %s", thing, pkg, name, rem) + } +} + +// zeroLiteral is a set of ast.BasicLit values that are zero values. +// It is not exhaustive. +var zeroLiteral = map[string]bool{ + "false": true, // bool + // runes + `'\x00'`: true, + `'\000'`: true, + // strings + `""`: true, + "``": true, + // numerics + "0": true, + "0.": true, + "0.0": true, + "0i": true, +} + +// knownWeakerTypes is a set of types that are commonly used to weaken var declarations. +// A common form of var declarations that legitimately mentions an explicit LHS type +// is where the LHS type is "weaker" than the exact RHS type, where "weaker" means an +// interface compared to a concrete type, or an interface compared to a superset interface. +// A canonical example is `var out io.Writer = os.Stdout`. +// This is only used when type checking fails to determine the exact types. +var knownWeakerTypes = map[string]bool{ + "io.Reader": true, + "io.Writer": true, + "proto.Message": true, +} + +// lintVarDecls examines variable declarations. It complains about declarations with +// redundant LHS types that can be inferred from the RHS. +func (f *file) lintVarDecls() { + var lastGen *ast.GenDecl // last GenDecl entered. + + f.walk(func(node ast.Node) bool { + switch v := node.(type) { + case *ast.GenDecl: + if v.Tok != token.CONST && v.Tok != token.VAR { + return false + } + lastGen = v + return true + case *ast.ValueSpec: + if lastGen.Tok == token.CONST { + return false + } + if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 { + return false + } + rhs := v.Values[0] + // An underscore var appears in a common idiom for compile-time interface satisfaction, + // as in "var _ Interface = (*Concrete)(nil)". + if isIdent(v.Names[0], "_") { + return false + } + // If the RHS is a zero value, suggest dropping it. + zero := false + if lit, ok := rhs.(*ast.BasicLit); ok { + zero = zeroLiteral[lit.Value] + } else if isIdent(rhs, "nil") { + zero = true + } + if zero { + f.errorf(rhs, 0.9, category("zero-value"), "should drop = %s from declaration of var %s; it is the zero value", f.render(rhs), v.Names[0]) + return false + } + lhsTyp := f.pkg.typeOf(v.Type) + rhsTyp := f.pkg.typeOf(rhs) + if lhsTyp != nil && rhsTyp != nil && !types.Identical(lhsTyp, rhsTyp) { + // Assignment to a different type is not redundant. + return false + } + + // The next three conditions are for suppressing the warning in situations + // where we were unable to typecheck. + + // If the LHS type is an interface, don't warn, since it is probably a + // concrete type on the RHS. Note that our feeble lexical check here + // will only pick up interface{} and other literal interface types; + // that covers most of the cases we care to exclude right now. + if _, ok := v.Type.(*ast.InterfaceType); ok { + return false + } + // If the RHS is an untyped const, only warn if the LHS type is its default type. + if defType, ok := f.isUntypedConst(rhs); ok && !isIdent(v.Type, defType) { + return false + } + // If the LHS is a known weaker type, and we couldn't type check both sides, + // don't warn. + if lhsTyp == nil || rhsTyp == nil { + if knownWeakerTypes[f.render(v.Type)] { + return false + } + } + + f.errorf(v.Type, 0.8, category("type-inference"), "should omit type %s from declaration of var %s; it will be inferred from the right-hand side", f.render(v.Type), v.Names[0]) + return false + } + return true + }) +} + +// lintElses examines else blocks. It complains about any else block whose if block ends in a return. +func (f *file) lintElses() { + // We don't want to flag if { } else if { } else { } constructions. + // They will appear as an IfStmt whose Else field is also an IfStmt. + // Record such a node so we ignore it when we visit it. + ignore := make(map[*ast.IfStmt]bool) + + f.walk(func(node ast.Node) bool { + ifStmt, ok := node.(*ast.IfStmt) + if !ok || ifStmt.Else == nil { + return true + } + if ignore[ifStmt] { + return true + } + if elseif, ok := ifStmt.Else.(*ast.IfStmt); ok { + ignore[elseif] = true + return true + } + if _, ok := ifStmt.Else.(*ast.BlockStmt); !ok { + // only care about elses without conditions + return true + } + if len(ifStmt.Body.List) == 0 { + return true + } + shortDecl := false // does the if statement have a ":=" initialization statement? + if ifStmt.Init != nil { + if as, ok := ifStmt.Init.(*ast.AssignStmt); ok && as.Tok == token.DEFINE { + shortDecl = true + } + } + lastStmt := ifStmt.Body.List[len(ifStmt.Body.List)-1] + if _, ok := lastStmt.(*ast.ReturnStmt); ok { + extra := "" + if shortDecl { + extra = " (move short variable declaration to its own line if necessary)" + } + f.errorf(ifStmt.Else, 1, link(styleGuideBase+"#indent-error-flow"), category("indent"), "if block ends with a return statement, so drop this else and outdent its block"+extra) + } + return true + }) +} + +// lintRanges examines range clauses. It complains about redundant constructions. +func (f *file) lintRanges() { + f.walk(func(node ast.Node) bool { + rs, ok := node.(*ast.RangeStmt) + if !ok { + return true + } + if rs.Value == nil { + // for x = range m { ... } + return true // single var form + } + if !isIdent(rs.Value, "_") { + // for ?, y = range m { ... } + return true + } + + p := f.errorf(rs.Value, 1, category("range-loop"), "should omit 2nd value from range; this loop is equivalent to `for %s %s range ...`", f.render(rs.Key), rs.Tok) + + newRS := *rs // shallow copy + newRS.Value = nil + p.ReplacementLine = f.firstLineOf(&newRS, rs) + + return true + }) +} + +// lintErrorf examines errors.New and testing.Error calls. It complains if its only argument is an fmt.Sprintf invocation. +func (f *file) lintErrorf() { + f.walk(func(node ast.Node) bool { + ce, ok := node.(*ast.CallExpr) + if !ok || len(ce.Args) != 1 { + return true + } + isErrorsNew := isPkgDot(ce.Fun, "errors", "New") + var isTestingError bool + se, ok := ce.Fun.(*ast.SelectorExpr) + if ok && se.Sel.Name == "Error" { + if typ := f.pkg.typeOf(se.X); typ != nil { + isTestingError = typ.String() == "*testing.T" + } + } + if !isErrorsNew && !isTestingError { + return true + } + arg := ce.Args[0] + ce, ok = arg.(*ast.CallExpr) + if !ok || !isPkgDot(ce.Fun, "fmt", "Sprintf") { + return true + } + errorfPrefix := "fmt" + if isTestingError { + errorfPrefix = f.render(se.X) + } + p := f.errorf(node, 1, category("errors"), "should replace %s(fmt.Sprintf(...)) with %s.Errorf(...)", f.render(se), errorfPrefix) + + m := f.srcLineWithMatch(ce, `^(.*)`+f.render(se)+`\(fmt\.Sprintf\((.*)\)\)(.*)$`) + if m != nil { + p.ReplacementLine = m[1] + errorfPrefix + ".Errorf(" + m[2] + ")" + m[3] + } + + return true + }) +} + +// lintErrors examines global error vars. It complains if they aren't named in the standard way. +func (f *file) lintErrors() { + for _, decl := range f.f.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.VAR { + continue + } + for _, spec := range gd.Specs { + spec := spec.(*ast.ValueSpec) + if len(spec.Names) != 1 || len(spec.Values) != 1 { + continue + } + ce, ok := spec.Values[0].(*ast.CallExpr) + if !ok { + continue + } + if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") { + continue + } + + id := spec.Names[0] + prefix := "err" + if id.IsExported() { + prefix = "Err" + } + if !strings.HasPrefix(id.Name, prefix) { + f.errorf(id, 0.9, category("naming"), "error var %s should have name of the form %sFoo", id.Name, prefix) + } + } + } +} + +func lintCapAndPunct(s string) (isCap, isPunct bool) { + first, firstN := utf8.DecodeRuneInString(s) + last, _ := utf8.DecodeLastRuneInString(s) + isPunct = last == '.' || last == ':' || last == '!' + isCap = unicode.IsUpper(first) + if isCap && len(s) > firstN { + // Don't flag strings starting with something that looks like an initialism. + if second, _ := utf8.DecodeRuneInString(s[firstN:]); unicode.IsUpper(second) { + isCap = false + } + } + return +} + +// lintErrorStrings examines error strings. It complains if they are capitalized or end in punctuation. +func (f *file) lintErrorStrings() { + f.walk(func(node ast.Node) bool { + ce, ok := node.(*ast.CallExpr) + if !ok { + return true + } + if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") { + return true + } + if len(ce.Args) < 1 { + return true + } + str, ok := ce.Args[0].(*ast.BasicLit) + if !ok || str.Kind != token.STRING { + return true + } + s, _ := strconv.Unquote(str.Value) // can assume well-formed Go + if s == "" { + return true + } + isCap, isPunct := lintCapAndPunct(s) + var msg string + switch { + case isCap && isPunct: + msg = "error strings should not be capitalized and should not end with punctuation" + case isCap: + msg = "error strings should not be capitalized" + case isPunct: + msg = "error strings should not end with punctuation" + default: + return true + } + // People use proper nouns and exported Go identifiers in error strings, + // so decrease the confidence of warnings for capitalization. + conf := 0.8 + if isCap { + conf = 0.6 + } + f.errorf(str, conf, link(styleGuideBase+"#error-strings"), category("errors"), msg) + return true + }) +} + +var badReceiverNames = map[string]bool{ + "me": true, + "this": true, + "self": true, +} + +// lintReceiverNames examines receiver names. It complains about inconsistent +// names used for the same type and names such as "this". +func (f *file) lintReceiverNames() { + typeReceiver := map[string]string{} + f.walk(func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 { + return true + } + names := fn.Recv.List[0].Names + if len(names) < 1 { + return true + } + name := names[0].Name + const ref = styleGuideBase + "#receiver-names" + if name == "_" { + f.errorf(n, 1, link(ref), category("naming"), `receiver name should not be an underscore`) + return true + } + if badReceiverNames[name] { + f.errorf(n, 1, link(ref), category("naming"), `receiver name should be a reflection of its identity; don't use generic names such as "me", "this", or "self"`) + return true + } + recv := receiverType(fn) + if prev, ok := typeReceiver[recv]; ok && prev != name { + f.errorf(n, 1, link(ref), category("naming"), "receiver name %s should be consistent with previous receiver name %s for %s", name, prev, recv) + return true + } + typeReceiver[recv] = name + return true + }) +} + +// lintIncDec examines statements that increment or decrement a variable. +// It complains if they don't use x++ or x--. +func (f *file) lintIncDec() { + f.walk(func(n ast.Node) bool { + as, ok := n.(*ast.AssignStmt) + if !ok { + return true + } + if len(as.Lhs) != 1 { + return true + } + if !isOne(as.Rhs[0]) { + return true + } + var suffix string + switch as.Tok { + case token.ADD_ASSIGN: + suffix = "++" + case token.SUB_ASSIGN: + suffix = "--" + default: + return true + } + f.errorf(as, 0.8, category("unary-op"), "should replace %s with %s%s", f.render(as), f.render(as.Lhs[0]), suffix) + return true + }) +} + +// lintMake examines statements that declare and initialize a variable with make. +// It complains if they are constructing a zero element slice. +func (f *file) lintMake() { + f.walk(func(n ast.Node) bool { + as, ok := n.(*ast.AssignStmt) + if !ok { + return true + } + // Only want single var := assignment statements. + if len(as.Lhs) != 1 || as.Tok != token.DEFINE { + return true + } + ce, ok := as.Rhs[0].(*ast.CallExpr) + if !ok { + return true + } + // Check if ce is make([]T, 0). + if !isIdent(ce.Fun, "make") || len(ce.Args) != 2 || !isZero(ce.Args[1]) { + return true + } + at, ok := ce.Args[0].(*ast.ArrayType) + if !ok || at.Len != nil { + return true + } + f.errorf(as, 0.8, category("slice"), `can probably use "var %s %s" instead`, f.render(as.Lhs[0]), f.render(at)) + return true + }) +} + +// lintErrorReturn examines function declarations that return an error. +// It complains if the error isn't the last parameter. +func (f *file) lintErrorReturn() { + f.walk(func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Type.Results == nil { + return true + } + ret := fn.Type.Results.List + if len(ret) <= 1 { + return true + } + // An error return parameter should be the last parameter. + // Flag any error parameters found before the last. + for _, r := range ret[:len(ret)-1] { + if isIdent(r.Type, "error") { + f.errorf(fn, 0.9, category("arg-order"), "error should be the last type when returning multiple items") + break // only flag one + } + } + return true + }) +} + +// lintUnexportedReturn examines exported function declarations. +// It complains if any return an unexported type. +func (f *file) lintUnexportedReturn() { + f.walk(func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok { + return true + } + if fn.Type.Results == nil { + return false + } + if !fn.Name.IsExported() { + return false + } + thing := "func" + if fn.Recv != nil && len(fn.Recv.List) > 0 { + thing = "method" + if !ast.IsExported(receiverType(fn)) { + // Don't report exported methods of unexported types, + // such as private implementations of sort.Interface. + return false + } + } + for _, ret := range fn.Type.Results.List { + typ := f.pkg.typeOf(ret.Type) + if exportedType(typ) { + continue + } + f.errorf(ret.Type, 0.8, category("unexported-type-in-api"), + "exported %s %s returns unexported type %s, which can be annoying to use", + thing, fn.Name.Name, typ) + break // only flag one + } + return false + }) +} + +// exportedType reports whether typ is an exported type. +// It is imprecise, and will err on the side of returning true, +// such as for composite types. +func exportedType(typ types.Type) bool { + switch T := typ.(type) { + case *types.Named: + // Builtin types have no package. + return T.Obj().Pkg() == nil || T.Obj().Exported() + case *types.Map: + return exportedType(T.Key()) && exportedType(T.Elem()) + case interface { + Elem() types.Type + }: // array, slice, pointer, chan + return exportedType(T.Elem()) + } + // Be conservative about other types, such as struct, interface, etc. + return true +} + +// timeSuffixes is a list of name suffixes that imply a time unit. +// This is not an exhaustive list. +var timeSuffixes = []string{ + "Sec", "Secs", "Seconds", + "Msec", "Msecs", + "Milli", "Millis", "Milliseconds", + "Usec", "Usecs", "Microseconds", + "MS", "Ms", +} + +func (f *file) lintTimeNames() { + f.walk(func(node ast.Node) bool { + v, ok := node.(*ast.ValueSpec) + if !ok { + return true + } + for _, name := range v.Names { + origTyp := f.pkg.typeOf(name) + // Look for time.Duration or *time.Duration; + // the latter is common when using flag.Duration. + typ := origTyp + if pt, ok := typ.(*types.Pointer); ok { + typ = pt.Elem() + } + if !f.pkg.isNamedType(typ, "time", "Duration") { + continue + } + suffix := "" + for _, suf := range timeSuffixes { + if strings.HasSuffix(name.Name, suf) { + suffix = suf + break + } + } + if suffix == "" { + continue + } + f.errorf(v, 0.9, category("time"), "var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix) + } + return true + }) +} + +func receiverType(fn *ast.FuncDecl) string { + switch e := fn.Recv.List[0].Type.(type) { + case *ast.Ident: + return e.Name + case *ast.StarExpr: + return e.X.(*ast.Ident).Name + } + panic(fmt.Sprintf("unknown method receiver AST node type %T", fn.Recv.List[0].Type)) +} + +func (f *file) walk(fn func(ast.Node) bool) { + ast.Walk(walker(fn), f.f) +} + +func (f *file) render(x interface{}) string { + var buf bytes.Buffer + if err := printer.Fprint(&buf, f.fset, x); err != nil { + panic(err) + } + return buf.String() +} + +func (f *file) debugRender(x interface{}) string { + var buf bytes.Buffer + if err := ast.Fprint(&buf, f.fset, x, nil); err != nil { + panic(err) + } + return buf.String() +} + +// walker adapts a function to satisfy the ast.Visitor interface. +// The function return whether the walk should proceed into the node's children. +type walker func(ast.Node) bool + +func (w walker) Visit(node ast.Node) ast.Visitor { + if w(node) { + return w + } + return nil +} + +func isIdent(expr ast.Expr, ident string) bool { + id, ok := expr.(*ast.Ident) + return ok && id.Name == ident +} + +// isBlank returns whether id is the blank identifier "_". +// If id == nil, the answer is false. +func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" } + +func isPkgDot(expr ast.Expr, pkg, name string) bool { + sel, ok := expr.(*ast.SelectorExpr) + return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name) +} + +func isZero(expr ast.Expr) bool { + lit, ok := expr.(*ast.BasicLit) + return ok && lit.Kind == token.INT && lit.Value == "0" +} + +func isOne(expr ast.Expr) bool { + lit, ok := expr.(*ast.BasicLit) + return ok && lit.Kind == token.INT && lit.Value == "1" +} + +var basicTypeKinds = map[types.BasicKind]string{ + types.UntypedBool: "bool", + types.UntypedInt: "int", + types.UntypedRune: "rune", + types.UntypedFloat: "float64", + types.UntypedComplex: "complex128", + types.UntypedString: "string", +} + +// isUntypedConst reports whether expr is an untyped constant, +// and indicates what its default type is. +// scope may be nil. +func (f *file) isUntypedConst(expr ast.Expr) (defType string, ok bool) { + // Re-evaluate expr outside of its context to see if it's untyped. + // (An expr evaluated within, for example, an assignment context will get the type of the LHS.) + exprStr := f.render(expr) + tv, err := types.Eval(f.fset, f.pkg.typesPkg, expr.Pos(), exprStr) + if err != nil { + return "", false + } + if b, ok := tv.Type.(*types.Basic); ok { + if dt, ok := basicTypeKinds[b.Kind()]; ok { + return dt, true + } + } + + return "", false +} + +// firstLineOf renders the given node and returns its first line. +// It will also match the indentation of another node. +func (f *file) firstLineOf(node, match ast.Node) string { + line := f.render(node) + if i := strings.Index(line, "\n"); i >= 0 { + line = line[:i] + } + return f.indentOf(match) + line +} + +func (f *file) indentOf(node ast.Node) string { + line := srcLine(f.src, f.fset.Position(node.Pos())) + for i, r := range line { + switch r { + case ' ', '\t': + default: + return line[:i] + } + } + return line // unusual or empty line +} + +func (f *file) srcLineWithMatch(node ast.Node, pattern string) (m []string) { + line := srcLine(f.src, f.fset.Position(node.Pos())) + line = strings.TrimSuffix(line, "\n") + rx := regexp.MustCompile(pattern) + return rx.FindStringSubmatch(line) +} + +// srcLine returns the complete line at p, including the terminating newline. +func srcLine(src []byte, p token.Position) string { + // Run to end of line in both directions if not at line start/end. + lo, hi := p.Offset, p.Offset+1 + for lo > 0 && src[lo-1] != '\n' { + lo-- + } + for hi < len(src) && src[hi-1] != '\n' { + hi++ + } + return string(src[lo:hi]) +} diff --git a/vendor/github.com/golang/lint/lint16.go b/vendor/github.com/golang/lint/lint16.go new file mode 100644 index 0000000000..34205aa821 --- /dev/null +++ b/vendor/github.com/golang/lint/lint16.go @@ -0,0 +1,17 @@ +// Copyright (c) 2016 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd. + +// +build go1.6 + +package lint + +import "go/types" + +// This is in its own file so it can be ignored under Go 1.5. + +func (i importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) { + return i.impFn(i.packages, path, srcDir) +} diff --git a/vendor/github.com/golang/lint/lint_test.go b/vendor/github.com/golang/lint/lint_test.go new file mode 100644 index 0000000000..c12370fda5 --- /dev/null +++ b/vendor/github.com/golang/lint/lint_test.go @@ -0,0 +1,290 @@ +// Copyright (c) 2013 The Go Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd. + +package lint + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "go/types" + "io/ioutil" + "path" + "regexp" + "strconv" + "strings" + "testing" +) + +var lintMatch = flag.String("lint.match", "", "restrict testdata matches to this pattern") + +func TestAll(t *testing.T) { + l := new(Linter) + rx, err := regexp.Compile(*lintMatch) + if err != nil { + t.Fatalf("Bad -lint.match value %q: %v", *lintMatch, err) + } + + baseDir := "testdata" + fis, err := ioutil.ReadDir(baseDir) + if err != nil { + t.Fatalf("ioutil.ReadDir: %v", err) + } + if len(fis) == 0 { + t.Fatalf("no files in %v", baseDir) + } + for _, fi := range fis { + if !rx.MatchString(fi.Name()) { + continue + } + //t.Logf("Testing %s", fi.Name()) + src, err := ioutil.ReadFile(path.Join(baseDir, fi.Name())) + if err != nil { + t.Fatalf("Failed reading %s: %v", fi.Name(), err) + } + + ins := parseInstructions(t, fi.Name(), src) + if ins == nil { + t.Errorf("Test file %v does not have instructions", fi.Name()) + continue + } + + ps, err := l.Lint(fi.Name(), src) + if err != nil { + t.Errorf("Linting %s: %v", fi.Name(), err) + continue + } + + for _, in := range ins { + ok := false + for i, p := range ps { + if p.Position.Line != in.Line { + continue + } + if in.Match.MatchString(p.Text) { + // check replacement if we are expecting one + if in.Replacement != "" { + // ignore any inline comments, since that would be recursive + r := p.ReplacementLine + if i := strings.Index(r, " //"); i >= 0 { + r = r[:i] + } + if r != in.Replacement { + t.Errorf("Lint failed at %s:%d; got replacement %q, want %q", fi.Name(), in.Line, r, in.Replacement) + } + } + + // remove this problem from ps + copy(ps[i:], ps[i+1:]) + ps = ps[:len(ps)-1] + + //t.Logf("/%v/ matched at %s:%d", in.Match, fi.Name(), in.Line) + ok = true + break + } + } + if !ok { + t.Errorf("Lint failed at %s:%d; /%v/ did not match", fi.Name(), in.Line, in.Match) + } + } + for _, p := range ps { + t.Errorf("Unexpected problem at %s:%d: %v", fi.Name(), p.Position.Line, p.Text) + } + } +} + +type instruction struct { + Line int // the line number this applies to + Match *regexp.Regexp // what pattern to match + Replacement string // what the suggested replacement line should be +} + +// parseInstructions parses instructions from the comments in a Go source file. +// It returns nil if none were parsed. +func parseInstructions(t *testing.T, filename string, src []byte) []instruction { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, filename, src, parser.ParseComments) + if err != nil { + t.Fatalf("Test file %v does not parse: %v", filename, err) + } + var ins []instruction + for _, cg := range f.Comments { + ln := fset.Position(cg.Pos()).Line + raw := cg.Text() + for _, line := range strings.Split(raw, "\n") { + if line == "" || strings.HasPrefix(line, "#") { + continue + } + if line == "OK" && ins == nil { + // so our return value will be non-nil + ins = make([]instruction, 0) + continue + } + if strings.Contains(line, "MATCH") { + rx, err := extractPattern(line) + if err != nil { + t.Fatalf("At %v:%d: %v", filename, ln, err) + } + matchLine := ln + if i := strings.Index(line, "MATCH:"); i >= 0 { + // This is a match for a different line. + lns := strings.TrimPrefix(line[i:], "MATCH:") + lns = lns[:strings.Index(lns, " ")] + matchLine, err = strconv.Atoi(lns) + if err != nil { + t.Fatalf("Bad match line number %q at %v:%d: %v", lns, filename, ln, err) + } + } + var repl string + if r, ok := extractReplacement(line); ok { + repl = r + } + ins = append(ins, instruction{ + Line: matchLine, + Match: rx, + Replacement: repl, + }) + } + } + } + return ins +} + +func extractPattern(line string) (*regexp.Regexp, error) { + a, b := strings.Index(line, "/"), strings.LastIndex(line, "/") + if a == -1 || a == b { + return nil, fmt.Errorf("malformed match instruction %q", line) + } + pat := line[a+1 : b] + rx, err := regexp.Compile(pat) + if err != nil { + return nil, fmt.Errorf("bad match pattern %q: %v", pat, err) + } + return rx, nil +} + +func extractReplacement(line string) (string, bool) { + // Look for this: / -> ` + // (the end of a match and start of a backtick string), + // and then the closing backtick. + const start = "/ -> `" + a, b := strings.Index(line, start), strings.LastIndex(line, "`") + if a < 0 || a > b { + return "", false + } + return line[a+len(start) : b], true +} + +func render(fset *token.FileSet, x interface{}) string { + var buf bytes.Buffer + if err := printer.Fprint(&buf, fset, x); err != nil { + panic(err) + } + return buf.String() +} + +func TestLine(t *testing.T) { + tests := []struct { + src string + offset int + want string + }{ + {"single line file", 5, "single line file"}, + {"single line file with newline\n", 5, "single line file with newline\n"}, + {"first\nsecond\nthird\n", 2, "first\n"}, + {"first\nsecond\nthird\n", 9, "second\n"}, + {"first\nsecond\nthird\n", 14, "third\n"}, + {"first\nsecond\nthird with no newline", 16, "third with no newline"}, + {"first byte\n", 0, "first byte\n"}, + } + for _, test := range tests { + got := srcLine([]byte(test.src), token.Position{Offset: test.offset}) + if got != test.want { + t.Errorf("srcLine(%q, offset=%d) = %q, want %q", test.src, test.offset, got, test.want) + } + } +} + +func TestLintName(t *testing.T) { + tests := []struct { + name, want string + }{ + {"foo_bar", "fooBar"}, + {"foo_bar_baz", "fooBarBaz"}, + {"Foo_bar", "FooBar"}, + {"foo_WiFi", "fooWiFi"}, + {"id", "id"}, + {"Id", "ID"}, + {"foo_id", "fooID"}, + {"fooId", "fooID"}, + {"fooUid", "fooUID"}, + {"idFoo", "idFoo"}, + {"uidFoo", "uidFoo"}, + {"midIdDle", "midIDDle"}, + {"APIProxy", "APIProxy"}, + {"ApiProxy", "APIProxy"}, + {"apiProxy", "apiProxy"}, + {"_Leading", "_Leading"}, + {"___Leading", "_Leading"}, + {"trailing_", "trailing"}, + {"trailing___", "trailing"}, + {"a_b", "aB"}, + {"a__b", "aB"}, + {"a___b", "aB"}, + {"Rpc1150", "RPC1150"}, + {"case3_1", "case3_1"}, + {"case3__1", "case3_1"}, + {"IEEE802_16bit", "IEEE802_16bit"}, + {"IEEE802_16Bit", "IEEE802_16Bit"}, + } + for _, test := range tests { + got := lintName(test.name) + if got != test.want { + t.Errorf("lintName(%q) = %q, want %q", test.name, got, test.want) + } + } +} + +func TestExportedType(t *testing.T) { + tests := []struct { + typString string + exp bool + }{ + {"int", true}, + {"string", false}, // references the shadowed builtin "string" + {"T", true}, + {"t", false}, + {"*T", true}, + {"*t", false}, + {"map[int]complex128", true}, + } + for _, test := range tests { + src := `package foo; type T int; type t int; type string struct{}` + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "foo.go", src, 0) + if err != nil { + t.Fatalf("Parsing %q: %v", src, err) + } + // use the package name as package path + config := &types.Config{} + pkg, err := config.Check(file.Name.Name, fset, []*ast.File{file}, nil) + if err != nil { + t.Fatalf("Type checking %q: %v", src, err) + } + tv, err := types.Eval(fset, pkg, token.NoPos, test.typString) + if err != nil { + t.Errorf("types.Eval(%q): %v", test.typString, err) + continue + } + if got := exportedType(tv.Type); got != test.exp { + t.Errorf("exportedType(%v) = %t, want %t", tv.Type, got, test.exp) + } + } +} diff --git a/vendor/github.com/golang/lint/misc/emacs/golint.el b/vendor/github.com/golang/lint/misc/emacs/golint.el new file mode 100644 index 0000000000..d015e3716d --- /dev/null +++ b/vendor/github.com/golang/lint/misc/emacs/golint.el @@ -0,0 +1,53 @@ +;;; golint.el --- lint for the Go source code + +;; Copyright 2013 The Go Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +;; URL: https://github.com/golang/lint + +;;; Commentary: + +;; To install golint, add the following lines to your .emacs file: +;; (add-to-list 'load-path "PATH CONTAINING golint.el" t) +;; (require 'golint) +;; +;; After this, type M-x golint on Go source code. +;; +;; Usage: +;; C-x ` +;; Jump directly to the line in your code which caused the first message. +;; +;; For more usage, see Compilation-Mode: +;; http://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html + +;;; Code: +(require 'compile) + +(defun go-lint-buffer-name (mode) + "*Golint*") + +(defun golint-process-setup () + "Setup compilation variables and buffer for `golint'." + (run-hooks 'golint-setup-hook)) + +(define-compilation-mode golint-mode "golint" + "Golint is a linter for Go source code." + (set (make-local-variable 'compilation-scroll-output) nil) + (set (make-local-variable 'compilation-disable-input) t) + (set (make-local-variable 'compilation-process-setup-function) + 'golint-process-setup) +) + +;;;###autoload +(defun golint () + "Run golint on the current file and populate the fix list. Pressing C-x ` will jump directly to the line in your code which caused the first message." + (interactive) + (compilation-start + (mapconcat #'shell-quote-argument + (list "golint" (expand-file-name buffer-file-name)) " ") + 'golint-mode)) + +(provide 'golint) + +;;; golint.el ends here diff --git a/vendor/github.com/golang/lint/misc/vim/ftplugin/go/lint.vim b/vendor/github.com/golang/lint/misc/vim/ftplugin/go/lint.vim new file mode 100644 index 0000000000..7dffd18176 --- /dev/null +++ b/vendor/github.com/golang/lint/misc/vim/ftplugin/go/lint.vim @@ -0,0 +1,31 @@ +" Copyright 2013 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" lint.vim: Vim command to lint Go files with golint. +" +" https://github.com/golang/lint +" +" This filetype plugin add a new commands for go buffers: +" +" :Lint +" +" Run golint for the current Go file. +" +if exists("b:did_ftplugin_go_lint") + finish +endif + +if !executable("golint") + finish +endif + +command! -buffer Lint call s:GoLint() + +function! s:GoLint() abort + cexpr system('golint ' . shellescape(expand('%'))) +endfunction + +let b:did_ftplugin_go_lint = 1 + +" vim:ts=4:sw=4:et diff --git a/vendor/github.com/golang/lint/testdata/4.go b/vendor/github.com/golang/lint/testdata/4.go new file mode 100644 index 0000000000..c9a6e89963 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/4.go @@ -0,0 +1,38 @@ +// Test that exported names have correct comments. + +// Package pkg does something. +package pkg + +import "time" + +type T int // MATCH /exported type T.*should.*comment.*or.*unexport/ + +func (T) F() {} // MATCH /exported method T\.F.*should.*comment.*or.*unexport/ + +// this is a nice type. +// MATCH /comment.*exported type U.*should.*form.*"U ..."/ +type U string + +// this is a neat function. +// MATCH /comment.*exported method U\.G.*should.*form.*"G ..."/ +func (U) G() {} + +// A V is a string. +type V string + +// V.H has a pointer receiver + +func (*V) H() {} // MATCH /exported method V\.H.*should.*comment.*or.*unexport/ + +var W = "foo" // MATCH /exported var W.*should.*comment.*or.*unexport/ + +const X = "bar" // MATCH /exported const X.*should.*comment.*or.*unexport/ + +var Y, Z int // MATCH /exported var Z.*own declaration/ + +// Location should be okay, since the other var name is an underscore. +var Location, _ = time.LoadLocation("Europe/Istanbul") // not Constantinople + +// this is improperly documented +// MATCH /comment.*const.*Thing.*form.*"Thing ..."/ +const Thing = "wonderful" diff --git a/vendor/github.com/golang/lint/testdata/5_test.go b/vendor/github.com/golang/lint/testdata/5_test.go new file mode 100644 index 0000000000..af174587c0 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/5_test.go @@ -0,0 +1,17 @@ +// This file ends in _test.go, so we should not warn about doc comments. +// OK + +package pkg + +import "testing" + +type H int + +func TestSomething(t *testing.T) { +} + +func TestSomething_suffix(t *testing.T) { +} + +func ExampleBuffer_reader() { +} diff --git a/vendor/github.com/golang/lint/testdata/blank-import-lib.go b/vendor/github.com/golang/lint/testdata/blank-import-lib.go new file mode 100644 index 0000000000..fe1eb5008a --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/blank-import-lib.go @@ -0,0 +1,39 @@ +// Test that blank imports in library packages are flagged. + +// Package foo ... +package foo + +// The instructions need to go before the imports below so they will not be +// mistaken for documentation. + +/* MATCH /blank import/ */ import _ "encoding/json" + +import ( + "fmt" + + /* MATCH /blank import/ */ _ "os" + + /* MATCH /blank import/ */ _ "net/http" + _ "path" +) + +import _ "encoding/base64" // Don't gripe about this + +import ( + // Don't gripe about these next two lines. + _ "compress/zlib" + _ "syscall" + + /* MATCH /blank import/ */ _ "path/filepath" +) + +import ( + "go/ast" + _ "go/scanner" // Don't gripe about this or the following line. + _ "go/token" +) + +var ( + _ fmt.Stringer // for "fmt" + _ ast.Node // for "go/ast" +) diff --git a/vendor/github.com/golang/lint/testdata/blank-import-lib_test.go b/vendor/github.com/golang/lint/testdata/blank-import-lib_test.go new file mode 100644 index 0000000000..abbf7d74df --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/blank-import-lib_test.go @@ -0,0 +1,25 @@ +// Test that blank imports in test packages are not flagged. +// OK + +// Package foo ... +package foo + +// These are essentially the same imports as in the "library" package, but +// these should not trigger the warning because this is a test. + +import _ "encoding/json" + +import ( + "fmt" + "testing" + + _ "os" + + _ "net/http" + _ "path" +) + +var ( + _ fmt.Stringer // for "fmt" + _ testing.T // for "testing" +) diff --git a/vendor/github.com/golang/lint/testdata/blank-import-main.go b/vendor/github.com/golang/lint/testdata/blank-import-main.go new file mode 100644 index 0000000000..6ec3a2423e --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/blank-import-main.go @@ -0,0 +1,14 @@ +// Test that blank imports in package main are not flagged. +// OK + +// Binary foo ... +package main + +import _ "fmt" + +import ( + "os" + _ "path" +) + +var _ os.File // for "os" diff --git a/vendor/github.com/golang/lint/testdata/broken.go b/vendor/github.com/golang/lint/testdata/broken.go new file mode 100644 index 0000000000..6543775c5a --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/broken.go @@ -0,0 +1,9 @@ +// Test of code that is malformed, but accepted by go/parser. +// See https://golang.org/issue/11271 for discussion. +// OK + +// Package pkg ... +package pkg + +// Foo is a method with a missing receiver. +func () Foo() {} diff --git a/vendor/github.com/golang/lint/testdata/common-methods.go b/vendor/github.com/golang/lint/testdata/common-methods.go new file mode 100644 index 0000000000..c0bb1363a5 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/common-methods.go @@ -0,0 +1,16 @@ +// Test that we don't nag for comments on common methods. +// OK + +// Package pkg ... +package pkg + +import "net/http" + +// T is ... +type T int + +func (T) Error() string { return "" } +func (T) String() string { return "" } +func (T) ServeHTTP(w http.ResponseWriter, r *http.Request) {} +func (T) Read(p []byte) (n int, err error) { return 0, nil } +func (T) Write(p []byte) (n int, err error) { return 0, nil } diff --git a/vendor/github.com/golang/lint/testdata/const-block.go b/vendor/github.com/golang/lint/testdata/const-block.go new file mode 100644 index 0000000000..4b89d6f60e --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/const-block.go @@ -0,0 +1,36 @@ +// Test for docs in const blocks + +// Package foo ... +package foo + +const ( + // Prefix for something. + // MATCH /InlineWhatever.*form/ + InlineWhatever = "blah" + + Whatsit = "missing_comment" // MATCH /Whatsit.*should have comment.*block/ + + // We should only warn once per block for missing comments, + // but always complain about malformed comments. + + WhosYourDaddy = "another_missing_one" + + // Something + // MATCH /WhatDoesHeDo.*form/ + WhatDoesHeDo = "it's not a tumor!" +) + +// These shouldn't need doc comments. +const ( + Alpha = "a" + Beta = "b" + Gamma = "g" +) + +// The comment on the previous const block shouldn't flow through to here. + +const UndocAgain = 6 // MATCH /UndocAgain.*should have comment/ + +const ( + SomeUndocumented = 7 // MATCH /SomeUndocumented.*should have comment.*block/ +) diff --git a/vendor/github.com/golang/lint/testdata/else-multi.go b/vendor/github.com/golang/lint/testdata/else-multi.go new file mode 100644 index 0000000000..98f39a3eb5 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/else-multi.go @@ -0,0 +1,18 @@ +// Test of return+else warning; should not trigger on multi-branch if/else. +// OK + +// Package pkg ... +package pkg + +import "log" + +func f(x int) bool { + if x == 0 { + log.Print("x is zero") + } else if x > 0 { + return true + } else { + log.Printf("non-positive x: %d", x) + } + return false +} diff --git a/vendor/github.com/golang/lint/testdata/else.go b/vendor/github.com/golang/lint/testdata/else.go new file mode 100644 index 0000000000..515c043d30 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/else.go @@ -0,0 +1,23 @@ +// Test of return+else warning. + +// Package pkg ... +package pkg + +import "log" + +func f(x int) bool { + if x > 0 { + return true + } else { // MATCH /if.*return.*else.*outdent/ + log.Printf("non-positive x: %d", x) + } + return false +} + +func g(f func() bool) string { + if ok := f(); ok { + return "it's okay" + } else { // MATCH /if.*return.*else.*outdent.*short.*var.*declaration/ + return "it's NOT okay!" + } +} diff --git a/vendor/github.com/golang/lint/testdata/error-return.go b/vendor/github.com/golang/lint/testdata/error-return.go new file mode 100644 index 0000000000..446a64c81a --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/error-return.go @@ -0,0 +1,43 @@ +// Test for returning errors. + +// Package foo ... +package foo + +// Returns nothing +func f() { // ok +} + +// Check for a single error return +func g() error { // ok + return nil +} + +// Check for a single other return type +func h() int { // ok + return 0 +} + +// Check for multiple return but error at end. +func i() (int, error) { // ok + return 0, nil +} + +// Check for multiple return but error at end with named variables. +func j() (x int, err error) { // ok + return 0, nil +} + +// Check for error in the wrong location on 2 types +func k() (error, int) { // MATCH /error should be the last type/ + return nil, 0 +} + +// Check for error in the wrong location for > 2 types +func l() (int, error, int) { // MATCH /error should be the last type/ + return 0, nil, 0 +} + +// Check for error in the wrong location with named variables. +func m() (x int, err error, y int) { // MATCH /error should be the last type/ + return 0, nil, 0 +} diff --git a/vendor/github.com/golang/lint/testdata/errorf.go b/vendor/github.com/golang/lint/testdata/errorf.go new file mode 100644 index 0000000000..72161f2b9e --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/errorf.go @@ -0,0 +1,40 @@ +// Test for not using fmt.Errorf or testing.Errorf. + +// Package foo ... +package foo + +import ( + "errors" + "fmt" + "testing" +) + +func f(x int) error { + if x > 10 { + return errors.New(fmt.Sprintf("something %d", x)) // MATCH /should replace.*errors\.New\(fmt\.Sprintf\(\.\.\.\)\).*fmt\.Errorf\(\.\.\.\)/ -> ` return fmt.Errorf("something %d", x)` + } + if x > 5 { + return errors.New(g("blah")) // ok + } + if x > 4 { + return errors.New("something else") // ok + } + return nil +} + +// TestF is a dummy test +func TestF(t *testing.T) error { + x := 1 + if x > 10 { + return t.Error(fmt.Sprintf("something %d", x)) // MATCH /should replace.*t\.Error\(fmt\.Sprintf\(\.\.\.\)\).*t\.Errorf\(\.\.\.\)/ + } + if x > 5 { + return t.Error(g("blah")) // ok + } + if x > 4 { + return t.Error("something else") // ok + } + return nil +} + +func g(s string) string { return "prefix: " + s } diff --git a/vendor/github.com/golang/lint/testdata/errors.go b/vendor/github.com/golang/lint/testdata/errors.go new file mode 100644 index 0000000000..396dcfc011 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/errors.go @@ -0,0 +1,36 @@ +// Test for naming errors. + +// Package foo ... +package foo + +import ( + "errors" + "fmt" +) + +var unexp = errors.New("some unexported error") // MATCH /error var.*unexp.*errFoo/ + +// Exp ... +var Exp = errors.New("some exported error") // MATCH /error var.*Exp.*ErrFoo/ + +var ( + e1 = fmt.Errorf("blah %d", 4) // MATCH /error var.*e1.*errFoo/ + // E2 ... + E2 = fmt.Errorf("blah %d", 5) // MATCH /error var.*E2.*ErrFoo/ +) + +func f() { + var whatever = errors.New("ok") // ok + _ = whatever +} + +// Check for the error strings themselves. + +func g(x int) error { + if x < 1 { + return fmt.Errorf("This %d is too low", x) // MATCH /error strings.*not be capitalized/ + } else if x == 0 { + return fmt.Errorf("XML time") // ok + } + return errors.New(`too much stuff.`) // MATCH /error strings.*not end with punctuation/ +} diff --git a/vendor/github.com/golang/lint/testdata/import-dot.go b/vendor/github.com/golang/lint/testdata/import-dot.go new file mode 100644 index 0000000000..879de26734 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/import-dot.go @@ -0,0 +1,8 @@ +// Test that dot imports are flagged. + +// Package pkg ... +package pkg + +import . "fmt" // MATCH /dot import/ + +var _ Stringer // from "fmt" diff --git a/vendor/github.com/golang/lint/testdata/inc.go b/vendor/github.com/golang/lint/testdata/inc.go new file mode 100644 index 0000000000..3868beea11 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/inc.go @@ -0,0 +1,14 @@ +// Test for use of x++ and x--. + +// Package pkg ... +package pkg + +func addOne(x int) int { + x += 1 // MATCH /x\+\+/ + return x +} + +func subOneInLoop(y int) { + for ; y > 0; y -= 1 { // MATCH /y--/ + } +} diff --git a/vendor/github.com/golang/lint/testdata/make.go b/vendor/github.com/golang/lint/testdata/make.go new file mode 100644 index 0000000000..eb5362a1fa --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/make.go @@ -0,0 +1,19 @@ +// Test for pointless make() calls. + +// Package pkg ... +package pkg + +import "net/http" + +// T is a test type. +type T int + +var z []T + +func f() { + x := make([]T, 0) // MATCH /var x \[\]T/ + y := make([]http.Request, 0) // MATCH /var y \[\]http\.Request/ + z = make([]T, 0) // ok, because we don't know where z is declared + + _, _ = x, y +} diff --git a/vendor/github.com/golang/lint/testdata/names.go b/vendor/github.com/golang/lint/testdata/names.go new file mode 100644 index 0000000000..4e84597b58 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/names.go @@ -0,0 +1,87 @@ +// Test for name linting. + +// Package pkg_with_underscores ... +package pkg_with_underscores // MATCH /underscore.*package name/ + +import ( + "io" + "net" + net_http "net/http" // renamed deliberately + "net/url" +) + +var var_name int // MATCH /underscore.*var.*var_name/ + +type t_wow struct { // MATCH /underscore.*type.*t_wow/ + x_damn int // MATCH /underscore.*field.*x_damn/ + Url *url.URL // MATCH /struct field.*Url.*URL/ +} + +const fooId = "blah" // MATCH /fooId.*fooID/ + +func f_it() { // MATCH /underscore.*func.*f_it/ + more_underscore := 4 // MATCH /underscore.*var.*more_underscore/ + _ = more_underscore + var err error + if isEof := (err == io.EOF); isEof { // MATCH /var.*isEof.*isEOF/ + more_underscore = 7 // should be okay + } + + x := net_http.Request{} // should be okay + _ = x + + var ips []net.IP + for _, theIp := range ips { // MATCH /range var.*theIp.*theIP/ + _ = theIp + } + + switch myJson := g(); { // MATCH /var.*myJson.*myJSON/ + default: + _ = myJson + } + var y net_http.ResponseWriter // an interface + switch tApi := y.(type) { // MATCH /var.*tApi.*tAPI/ + default: + _ = tApi + } + + var c chan int + select { + case qId := <-c: // MATCH /var.*qId.*qID/ + _ = qId + } +} + +// Common styles in other languages that don't belong in Go. +const ( + CPP_CONST = 1 // MATCH /ALL_CAPS.*CamelCase/ + kLeadingKay = 2 // MATCH /k.*leadingKay/ + + HTML = 3 // okay; no underscore + X509B = 4 // ditto +) + +func f(bad_name int) {} // MATCH /underscore.*func parameter.*bad_name/ +func g() (no_way int) { return 0 } // MATCH /underscore.*func result.*no_way/ +func (t *t_wow) f(more_under string) {} // MATCH /underscore.*method parameter.*more_under/ +func (t *t_wow) g() (still_more string) { return "" } // MATCH /underscore.*method result.*still_more/ + +type i interface { + CheckHtml() string // okay; interface method names are often constrained by the concrete types' method names + + F(foo_bar int) // MATCH /foo_bar.*fooBar/ +} + +// All okay; underscore between digits +const case1_1 = 1 + +type case2_1 struct { + case2_2 int +} + +func case3_1(case3_2 int) (case3_3 string) { + case3_4 := 4 + _ = case3_4 + + return "" +} diff --git a/vendor/github.com/golang/lint/testdata/pkg-doc1.go b/vendor/github.com/golang/lint/testdata/pkg-doc1.go new file mode 100644 index 0000000000..8197a8ee0f --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/pkg-doc1.go @@ -0,0 +1,3 @@ +// Test of missing package comment. + +package foo // MATCH /should.*package comment.*unless/ diff --git a/vendor/github.com/golang/lint/testdata/pkg-doc2.go b/vendor/github.com/golang/lint/testdata/pkg-doc2.go new file mode 100644 index 0000000000..c61febd0e9 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/pkg-doc2.go @@ -0,0 +1,5 @@ +// Test of package comment in an incorrect form. + +// Some random package doc that isn't in the right form. +// MATCH /package comment should.*form.*"Package testdata .*"/ +package testdata diff --git a/vendor/github.com/golang/lint/testdata/pkg-doc3.go b/vendor/github.com/golang/lint/testdata/pkg-doc3.go new file mode 100644 index 0000000000..95e814e0a4 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/pkg-doc3.go @@ -0,0 +1,7 @@ +// Test of block package comment. +// OK + +/* +Package foo is pretty sweet. +*/ +package foo diff --git a/vendor/github.com/golang/lint/testdata/pkg-doc4.go b/vendor/github.com/golang/lint/testdata/pkg-doc4.go new file mode 100644 index 0000000000..23448dec31 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/pkg-doc4.go @@ -0,0 +1,7 @@ +// Test of block package comment with leading space. + +/* + Package foo is pretty sweet. +MATCH /package comment.*leading space/ +*/ +package foo diff --git a/vendor/github.com/golang/lint/testdata/pkg-doc5.go b/vendor/github.com/golang/lint/testdata/pkg-doc5.go new file mode 100644 index 0000000000..cbe5d1e6c9 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/pkg-doc5.go @@ -0,0 +1,9 @@ +// Test of detached package comment. + +/* +Package foo is pretty sweet. +*/ + +package foo + +// MATCH:6 /package comment.*detached/ diff --git a/vendor/github.com/golang/lint/testdata/pkg-main.go b/vendor/github.com/golang/lint/testdata/pkg-main.go new file mode 100644 index 0000000000..c261945d69 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/pkg-main.go @@ -0,0 +1,5 @@ +// Test of package comment for package main. +// OK + +// This binary does something awesome. +package main diff --git a/vendor/github.com/golang/lint/testdata/range.go b/vendor/github.com/golang/lint/testdata/range.go new file mode 100644 index 0000000000..af36a82e02 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/range.go @@ -0,0 +1,37 @@ +// Test for range construction. + +// Package foo ... +package foo + +func f() { + var m map[string]int + + // with := + for x, _ := range m { // MATCH /should omit 2nd value.*range.*equivalent.*for x := range/ -> ` for x := range m {` + _ = x + } + // with = + var y string + _ = y + for y, _ = range m { // MATCH /should omit 2nd value.*range.*equivalent.*for y = range/ + } + + // all OK: + for x := range m { + _ = x + } + for x, y := range m { + _, _ = x, y + } + for _, y := range m { + _ = y + } + var x int + _ = x + for y = range m { + } + for y, x = range m { + } + for _, x = range m { + } +} diff --git a/vendor/github.com/golang/lint/testdata/receiver-names.go b/vendor/github.com/golang/lint/testdata/receiver-names.go new file mode 100644 index 0000000000..e5f3fa27e2 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/receiver-names.go @@ -0,0 +1,41 @@ +// Test for bad receiver names. + +// Package foo ... +package foo + +type foo struct{} + +func (this foo) f1() { // MATCH /should be a reflection of its identity/ +} + +func (self foo) f2() { // MATCH /should be a reflection of its identity/ +} + +func (f foo) f3() { +} + +func (foo) f4() { +} + +type bar struct{} + +func (b bar) f1() { +} + +func (b bar) f2() { +} + +func (a bar) f3() { // MATCH /receiver name a should be consistent with previous receiver name b for bar/ +} + +func (a *bar) f4() { // MATCH /receiver name a should be consistent with previous receiver name b for bar/ +} + +func (b *bar) f5() { +} + +func (bar) f6() { +} + +func (_ *bar) f7() { // MATCH /receiver name should not be an underscore/ +} diff --git a/vendor/github.com/golang/lint/testdata/sort.go b/vendor/github.com/golang/lint/testdata/sort.go new file mode 100644 index 0000000000..c099049428 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/sort.go @@ -0,0 +1,20 @@ +// Test that we don't ask for comments on sort.Interface methods. + +// Package pkg ... +package pkg + +// T is ... +type T []int + +// Len by itself should get documented. + +func (t T) Len() int { return len(t) } // MATCH /exported method T\.Len.*should.*comment/ + +// U is ... +type U []int + +func (u U) Len() int { return len(u) } +func (u U) Less(i, j int) bool { return u[i] < u[j] } +func (u U) Swap(i, j int) { u[i], u[j] = u[j], u[i] } + +func (u U) Other() {} // MATCH /exported method U\.Other.*should.*comment/ diff --git a/vendor/github.com/golang/lint/testdata/stutter.go b/vendor/github.com/golang/lint/testdata/stutter.go new file mode 100644 index 0000000000..3e6b13e7b0 --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/stutter.go @@ -0,0 +1,25 @@ +// Test of stuttery names. + +// Package donut ... +package donut + +// DonutMaker makes donuts. +type DonutMaker struct{} // MATCH /donut\.DonutMaker.*stutter/ + +// DonutRank computes the ranking of a donut. +func DonutRank(d Donut) int { // MATCH /donut\.DonutRank.*stutter/ + return 0 +} + +// Donut is a delicious treat. +type Donut struct{} // ok because it is the whole name + +// Donuts are great, aren't they? +type Donuts []Donut // ok because it didn't start a new word + +type donutGlaze int // ok because it is unexported + +// DonutMass reports the mass of a donut. +func (d *Donut) DonutMass() (grams int) { // okay because it is a method + return 38 +} diff --git a/vendor/github.com/golang/lint/testdata/time.go b/vendor/github.com/golang/lint/testdata/time.go new file mode 100644 index 0000000000..9271acb6ae --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/time.go @@ -0,0 +1,13 @@ +// Test of time suffixes. + +// Package foo ... +package foo + +import ( + "flag" + "time" +) + +var rpcTimeoutMsec = flag.Duration("rpc_timeout", 100*time.Millisecond, "some flag") // MATCH /Msec.*\*time.Duration/ + +var timeoutSecs = 5 * time.Second // MATCH /Secs.*time.Duration/ diff --git a/vendor/github.com/golang/lint/testdata/unexp-return.go b/vendor/github.com/golang/lint/testdata/unexp-return.go new file mode 100644 index 0000000000..7fc9769f6d --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/unexp-return.go @@ -0,0 +1,46 @@ +// Test for unexported return types. + +// Package foo ... +package foo + +import () + +type hidden struct{} + +// Exported returns a hidden type, which is annoying. +func Exported() hidden { // MATCH /Exported.*unexported.*hidden/ + return hidden{} +} + +// ExpErr returns a builtin type. +func ExpErr() error { // ok +} + +func (hidden) ExpOnHidden() hidden { // ok +} + +// T is another test type. +type T struct{} + +// MethodOnT returns a hidden type, which is annoying. +func (T) MethodOnT() hidden { // MATCH /method MethodOnT.*unexported.*hidden/ + return hidden{} +} + +// ExpT returns a T. +func ExpT() T { // ok + return T{} +} + +func unexp() hidden { // ok + return hidden{} +} + +// This is slightly sneaky: we shadow the builtin "int" type. + +type int struct{} + +// ExportedIntReturner returns an unexported type from this package. +func ExportedIntReturner() int { // MATCH /ExportedIntReturner.*unexported.*int/ + return int{} +} diff --git a/vendor/github.com/golang/lint/testdata/var-decl.go b/vendor/github.com/golang/lint/testdata/var-decl.go new file mode 100644 index 0000000000..d382bee88c --- /dev/null +++ b/vendor/github.com/golang/lint/testdata/var-decl.go @@ -0,0 +1,80 @@ +// Test for redundant type declaration. + +// Package foo ... +package foo + +import "fmt" +import "net/http" + +// Q is a test type. +type Q bool + +var myInt int = 7 // MATCH /should.*int.*myInt.*inferred/ +var mux *http.ServeMux = http.NewServeMux() // MATCH /should.*\*http\.ServeMux.*inferred/ + +var myZeroInt int = 0 // MATCH /should.*= 0.*myZeroInt.*zero value/ +var myZeroFlt float32 = 0. // MATCH /should.*= 0\..*myZeroFlt.*zero value/ +var myZeroF64 float64 = 0.0 // MATCH /should.*= 0\..*myZeroF64.*zero value/ +var myZeroImg complex64 = 0i // MATCH /should.*= 0i.*myZeroImg.*zero value/ +var myZeroStr string = "" // MATCH /should.*= "".*myZeroStr.*zero value/ +var myZeroRaw string = `` // MATCH /should.*= ``.*myZeroRaw.*zero value/ +var myZeroPtr *Q = nil // MATCH /should.*= nil.*myZeroPtr.*zero value/ +var myZeroRune rune = '\x00' // MATCH /should.*= '\\x00'.*myZeroRune.*zero value/ +var myZeroRune2 rune = '\000' // MATCH /should.*= '\\000'.*myZeroRune2.*zero value/ + +// No warning because there's no type on the LHS +var x = 0 + +// This shouldn't get a warning because there's no initial values. +var str fmt.Stringer + +// No warning because this is a const. +const k uint64 = 7 + +const num = 123 + +// No warning because the var's RHS is known to be an untyped const. +var flags uint32 = num + +// No warnings because the RHS is an ideal int, and the LHS is a different int type. +var userID int64 = 1235 +var negID int64 = -1 +var parenID int64 = (17) +var crazyID int64 = -(-(-(-9))) + +// Same, but for strings and floats. +type stringT string +type floatT float64 + +var stringV stringT = "abc" +var floatV floatT = 123.45 + +// No warning because the LHS names an interface type. +var data interface{} = googleIPs +var googleIPs []int + +// No warning because it's a common idiom for interface satisfaction. +var _ Server = (*serverImpl)(nil) + +// Server is a test type. +type Server interface{} +type serverImpl struct{} + +// LHS is a different type than the RHS. +var myStringer fmt.Stringer = q(0) + +// We don't figure out the true types of LHS and RHS here, +// but io.Writer is a known weaker type for many common uses, +// so the suggestion should be suppressed here. +var out io.Writer = os.Stdout + +// This next one, however, should be type checked. +var out2 io.Writer = newWriter() // MATCH /should.*io\.Writer/ + +func newWriter() io.Writer { return nil } + +var y string = q(1).String() // MATCH /should.*string/ + +type q int + +func (q) String() string { return "I'm a q" } diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go index 19235cf26a..7350678d71 100644 --- a/vendor/golang.org/x/net/context/context.go +++ b/vendor/golang.org/x/net/context/context.go @@ -36,12 +36,7 @@ // Contexts. package context // import "golang.org/x/net/context" -import ( - "errors" - "fmt" - "sync" - "time" -) +import "time" // A Context carries a deadline, a cancelation signal, and other values across // API boundaries. @@ -138,48 +133,6 @@ type Context interface { Value(key interface{}) interface{} } -// Canceled is the error returned by Context.Err when the context is canceled. -var Canceled = errors.New("context canceled") - -// DeadlineExceeded is the error returned by Context.Err when the context's -// deadline passes. -var DeadlineExceeded = errors.New("context deadline exceeded") - -// An emptyCtx is never canceled, has no values, and has no deadline. It is not -// struct{}, since vars of this type must have distinct addresses. -type emptyCtx int - -func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { - return -} - -func (*emptyCtx) Done() <-chan struct{} { - return nil -} - -func (*emptyCtx) Err() error { - return nil -} - -func (*emptyCtx) Value(key interface{}) interface{} { - return nil -} - -func (e *emptyCtx) String() string { - switch e { - case background: - return "context.Background" - case todo: - return "context.TODO" - } - return "unknown empty Context" -} - -var ( - background = new(emptyCtx) - todo = new(emptyCtx) -) - // Background returns a non-nil, empty Context. It is never canceled, has no // values, and has no deadline. It is typically used by the main function, // initialization, and tests, and as the top-level Context for incoming @@ -201,247 +154,3 @@ func TODO() Context { // A CancelFunc does not wait for the work to stop. // After the first call, subsequent calls to a CancelFunc do nothing. type CancelFunc func() - -// WithCancel returns a copy of parent with a new Done channel. The returned -// context's Done channel is closed when the returned cancel function is called -// or when the parent context's Done channel is closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { - c := newCancelCtx(parent) - propagateCancel(parent, c) - return c, func() { c.cancel(true, Canceled) } -} - -// newCancelCtx returns an initialized cancelCtx. -func newCancelCtx(parent Context) *cancelCtx { - return &cancelCtx{ - Context: parent, - done: make(chan struct{}), - } -} - -// propagateCancel arranges for child to be canceled when parent is. -func propagateCancel(parent Context, child canceler) { - if parent.Done() == nil { - return // parent is never canceled - } - if p, ok := parentCancelCtx(parent); ok { - p.mu.Lock() - if p.err != nil { - // parent has already been canceled - child.cancel(false, p.err) - } else { - if p.children == nil { - p.children = make(map[canceler]bool) - } - p.children[child] = true - } - p.mu.Unlock() - } else { - go func() { - select { - case <-parent.Done(): - child.cancel(false, parent.Err()) - case <-child.Done(): - } - }() - } -} - -// parentCancelCtx follows a chain of parent references until it finds a -// *cancelCtx. This function understands how each of the concrete types in this -// package represents its parent. -func parentCancelCtx(parent Context) (*cancelCtx, bool) { - for { - switch c := parent.(type) { - case *cancelCtx: - return c, true - case *timerCtx: - return c.cancelCtx, true - case *valueCtx: - parent = c.Context - default: - return nil, false - } - } -} - -// removeChild removes a context from its parent. -func removeChild(parent Context, child canceler) { - p, ok := parentCancelCtx(parent) - if !ok { - return - } - p.mu.Lock() - if p.children != nil { - delete(p.children, child) - } - p.mu.Unlock() -} - -// A canceler is a context type that can be canceled directly. The -// implementations are *cancelCtx and *timerCtx. -type canceler interface { - cancel(removeFromParent bool, err error) - Done() <-chan struct{} -} - -// A cancelCtx can be canceled. When canceled, it also cancels any children -// that implement canceler. -type cancelCtx struct { - Context - - done chan struct{} // closed by the first cancel call. - - mu sync.Mutex - children map[canceler]bool // set to nil by the first cancel call - err error // set to non-nil by the first cancel call -} - -func (c *cancelCtx) Done() <-chan struct{} { - return c.done -} - -func (c *cancelCtx) Err() error { - c.mu.Lock() - defer c.mu.Unlock() - return c.err -} - -func (c *cancelCtx) String() string { - return fmt.Sprintf("%v.WithCancel", c.Context) -} - -// cancel closes c.done, cancels each of c's children, and, if -// removeFromParent is true, removes c from its parent's children. -func (c *cancelCtx) cancel(removeFromParent bool, err error) { - if err == nil { - panic("context: internal error: missing cancel error") - } - c.mu.Lock() - if c.err != nil { - c.mu.Unlock() - return // already canceled - } - c.err = err - close(c.done) - for child := range c.children { - // NOTE: acquiring the child's lock while holding parent's lock. - child.cancel(false, err) - } - c.children = nil - c.mu.Unlock() - - if removeFromParent { - removeChild(c.Context, c) - } -} - -// WithDeadline returns a copy of the parent context with the deadline adjusted -// to be no later than d. If the parent's deadline is already earlier than d, -// WithDeadline(parent, d) is semantically equivalent to parent. The returned -// context's Done channel is closed when the deadline expires, when the returned -// cancel function is called, or when the parent context's Done channel is -// closed, whichever happens first. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { - if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { - // The current deadline is already sooner than the new one. - return WithCancel(parent) - } - c := &timerCtx{ - cancelCtx: newCancelCtx(parent), - deadline: deadline, - } - propagateCancel(parent, c) - d := deadline.Sub(time.Now()) - if d <= 0 { - c.cancel(true, DeadlineExceeded) // deadline has already passed - return c, func() { c.cancel(true, Canceled) } - } - c.mu.Lock() - defer c.mu.Unlock() - if c.err == nil { - c.timer = time.AfterFunc(d, func() { - c.cancel(true, DeadlineExceeded) - }) - } - return c, func() { c.cancel(true, Canceled) } -} - -// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to -// implement Done and Err. It implements cancel by stopping its timer then -// delegating to cancelCtx.cancel. -type timerCtx struct { - *cancelCtx - timer *time.Timer // Under cancelCtx.mu. - - deadline time.Time -} - -func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { - return c.deadline, true -} - -func (c *timerCtx) String() string { - return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) -} - -func (c *timerCtx) cancel(removeFromParent bool, err error) { - c.cancelCtx.cancel(false, err) - if removeFromParent { - // Remove this timerCtx from its parent cancelCtx's children. - removeChild(c.cancelCtx.Context, c) - } - c.mu.Lock() - if c.timer != nil { - c.timer.Stop() - c.timer = nil - } - c.mu.Unlock() -} - -// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete: -// -// func slowOperationWithTimeout(ctx context.Context) (Result, error) { -// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) -// defer cancel() // releases resources if slowOperation completes before timeout elapses -// return slowOperation(ctx) -// } -func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { - return WithDeadline(parent, time.Now().Add(timeout)) -} - -// WithValue returns a copy of parent in which the value associated with key is -// val. -// -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. -func WithValue(parent Context, key interface{}, val interface{}) Context { - return &valueCtx{parent, key, val} -} - -// A valueCtx carries a key-value pair. It implements Value for that key and -// delegates all other calls to the embedded Context. -type valueCtx struct { - Context - key, val interface{} -} - -func (c *valueCtx) String() string { - return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) -} - -func (c *valueCtx) Value(key interface{}) interface{} { - if c.key == key { - return c.val - } - return c.Context.Value(key) -} diff --git a/vendor/golang.org/x/net/context/context_test.go b/vendor/golang.org/x/net/context/context_test.go index 05345fc5e5..fa7309631d 100644 --- a/vendor/golang.org/x/net/context/context_test.go +++ b/vendor/golang.org/x/net/context/context_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !go1.7 + package context import ( diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go new file mode 100644 index 0000000000..f8cda19ada --- /dev/null +++ b/vendor/golang.org/x/net/context/go17.go @@ -0,0 +1,72 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package context + +import ( + "context" // standard library's context, as of Go 1.7 + "time" +) + +var ( + todo = context.TODO() + background = context.Background() +) + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = context.Canceled + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = context.DeadlineExceeded + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + ctx, f := context.WithCancel(parent) + return ctx, CancelFunc(f) +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + ctx, f := context.WithDeadline(parent, deadline) + return ctx, CancelFunc(f) +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return context.WithValue(parent, key, val) +} diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go new file mode 100644 index 0000000000..5a30acabd0 --- /dev/null +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -0,0 +1,300 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.7 + +package context + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, c) + return c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) *cancelCtx { + return &cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + *cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(false, err) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +} diff --git a/vendor/golang.org/x/net/websocket/websocket.go b/vendor/golang.org/x/net/websocket/websocket.go index e069b33008..9412191ded 100644 --- a/vendor/golang.org/x/net/websocket/websocket.go +++ b/vendor/golang.org/x/net/websocket/websocket.go @@ -209,9 +209,6 @@ func (ws *Conn) Write(msg []byte) (n int, err error) { } n, err = w.Write(msg) w.Close() - if err != nil { - return n, err - } return n, err } diff --git a/version/version.go b/version/version.go index 985720b2e9..b29189d395 100644 --- a/version/version.go +++ b/version/version.go @@ -4,6 +4,8 @@ package version +// Version information that is displayed by the "version" command and used to +// identify the version of running instances of OPA. var ( Version = "0.1.0-dev" Vcs = ""