├── .reviewdog.yml ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── analyzer.go ├── analyzer_test.go ├── checker.go ├── cmd └── wraperr │ └── main.go ├── go.mod ├── go.sum ├── testdata └── src │ ├── clousure │ └── main.go │ ├── generated │ └── main.go │ ├── multi_named_return │ └── main.go │ ├── multi_return │ └── main.go │ ├── named_return │ └── main.go │ ├── otherpkg_select_stmt │ ├── func.go │ ├── interface.go │ ├── otherpkg │ │ └── otherpkg.go │ └── struct.go │ ├── select_stmt │ └── struct.go │ ├── simple │ ├── main.go │ └── main_test.go │ └── vendor │ └── github.com │ └── pkg │ └── errors │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── appveyor.yml │ ├── errors.go │ └── stack.go └── util.go /.reviewdog.yml: -------------------------------------------------------------------------------- 1 | runner: 2 | golint: 3 | cmd: golint $(go list ./... | grep -v /vendor/) 4 | format: golint 5 | govet: 6 | cmd: go vet $(go list ./... | grep -v /vendor/) 7 | format: govet 8 | errcheck: 9 | cmd: errcheck -asserts -ignoretests -blank ./... 10 | errorformat: 11 | - "%f:%l:%c:%m" 12 | wraperr: 13 | cmd: wraperr ./... 14 | errorformat: 15 | - "%f:%l:%c:%m" 16 | megacheck: 17 | cmd: megacheck ./... 18 | errorformat: 19 | - "%f:%l:%c:%m" 20 | unparam: 21 | cmd: unparam ./... 22 | errorformat: 23 | - "%f:%l:%c: %m" 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: "1.13" 3 | 4 | env: 5 | global: 6 | - GO111MODULE=on 7 | 8 | # REVIEWDOG_GITHUB_API_TOKEN 9 | - secure: "t48yClc+qmha3iyjwMwJnYemOTf5SwdQj5I+70/gSwNhVPt+8tgRuAbb3NiWlSSNulFpBxyjaslNZU/XBQf4ZxsDuVGQozHyn3B7lLP9As3yYkgiumuOMCESm9z48VnBS8Qj+8nnQaaTebxyQe03DM9x26q5a5xjlOen/YroIErw2OYgs/UsrYeWjJUY5g60vDuoaxJ6CVDVf2S2ChHx407WcYccBQ8q3AgBkxmiTlW7Ai8mE34P5nQGsgO/3vkiPh0U3nAZl7lZ8lClbY3c7GZc7awT47+l3ZdBxk5KyxwFLQYJDVgpzuFROfWy2Idxw02xAoaowappOEYy8ER/zXY2osvOOhoHjgeWK/dVLOYSUQdEMfpotl3e+riS6IdX0uzuQjeIzDCDpHhWdHOaZLENREMx2RGKvnUHDPmMxrdDObTpwdtOmEoR0rY259m25YnVMYlz9raVOJzZ++v+c4mHbxfD53xzexorZswO49WG/mlt9stOdMgh8htm2Y06vFe4e8SDrCEEs+a0S4S2XAyNbD2LkaKhgXaTbDnz5kZ87xX//fbz4AjUavWYVVpvvH74ghPMW47risaLjfNklCdiKi7O/K7S6D/1mfw93K4YmbOwPRnnHhjR2AXOUlDoC28QGVMuY8kK4Oj2FCBxMRH/9xqYPUpDCl/OcQ6g/Po=" 10 | 11 | cache: 12 | directories: 13 | - $GOPATH/pkg/mod 14 | 15 | jobs: 16 | include: 17 | - name: 'lint' 18 | script: make lint 19 | if: type = 'pull_request' 20 | 21 | - script: make cover 22 | after_success: bash <(curl -s https://codecov.io/bash) 23 | if: type != 'pull_request' 24 | 25 | - &test 26 | go: "1.12" 27 | script: make test 28 | if: type != 'pull_request' 29 | 30 | - <<: *test 31 | go: "1.11" 32 | 33 | - <<: *test 34 | go: "master" 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Masayuki Izumi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PATH := ${PWD}/bin:${PATH} 2 | export PATH 3 | 4 | # linters 5 | bin/reviewdog: 6 | curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b ./bin v0.9.13 7 | 8 | bin/golangci-lint: 9 | curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ./bin v1.19.0 10 | 11 | # Commands 12 | #---------------------------------------------------------------- 13 | 14 | .PHONY: lint 15 | lint: ./bin/reviewdog ./bin/golangci-lint 16 | ifdef CI 17 | reviewdog -reporter=github-pr-review 18 | else 19 | reviewdog -diff="git diff master" 20 | endif 21 | 22 | .PHONY: test 23 | test: 24 | go test -v ./... 25 | 26 | .PHONY: cover 27 | cover: 28 | go test -v -coverprofile coverage.txt -covermode atomic ./... 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wraperr 2 | [![Build Status](https://travis-ci.com/srvc/wraperr.svg?branch=master)](https://travis-ci.com/srvc/wraperr) 3 | [![codecov](https://codecov.io/gh/srvc/wraperr/branch/master/graph/badge.svg)](https://codecov.io/gh/srvc/wraperr) 4 | [![Go project version](https://badge.fury.io/go/github.com%2Fsrvc%2Fwraperr.svg)](https://badge.fury.io/go/github.com%2Fsrvc%2Fwraperr) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/srvc/wraperr)](https://goreportcard.com/report/github.com/srvc/wraperr) 6 | [![license](https://img.shields.io/github/license/srvc/wraperr.svg)](./LICENSE) 7 | 8 | Check that error return value are wrapped 9 | 10 | ## Install 11 | 12 | ``` 13 | go get -u github.com/srvc/wraperr/cmd/wraperr 14 | ``` 15 | 16 | 17 | ## Usage 18 | 19 | To check all packages beneath the current directory: 20 | 21 | ``` 22 | wraperr ./... 23 | ``` 24 | 25 | 26 | ## Inspired 27 | 28 | - [errcheck](https://github.com/kisielk/errcheck) 29 | -------------------------------------------------------------------------------- /analyzer.go: -------------------------------------------------------------------------------- 1 | package wraperr 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | 7 | "golang.org/x/tools/go/analysis" 8 | "golang.org/x/tools/go/analysis/passes/inspect" 9 | "golang.org/x/tools/go/ast/inspector" 10 | ) 11 | 12 | var Analyzer = &analysis.Analyzer{ 13 | Name: "wraperr", 14 | Doc: "Check that error return value are wrapped", 15 | Requires: []*analysis.Analyzer{inspect.Analyzer}, 16 | Run: runAnalyze, 17 | } 18 | 19 | var ( 20 | // WrapperFuncList contains "path/to/pkg.Funcname" strings that adds a context to errors. 21 | WrapperFuncList = []string{ 22 | // stdlibs 23 | "errors.New", 24 | "fmt.Errorf", 25 | 26 | // github.com/pkg/errors 27 | "github.com/pkg/errors.Errorf", 28 | "github.com/pkg/errors.New", 29 | "github.com/pkg/errors.WithMessage", 30 | "github.com/pkg/errors.WithStack", 31 | "github.com/pkg/errors.Wrap", 32 | "github.com/pkg/errors.Wrapf", 33 | 34 | // github.com/srvc/fail 35 | "github.com/srvc/fail.Errorf", 36 | "github.com/srvc/fail.New", 37 | "github.com/srvc/fail.Wrap", 38 | } 39 | ) 40 | 41 | type errIdent struct { 42 | *ast.Ident 43 | wrapped bool 44 | } 45 | 46 | var wrapperFuncSet map[string]struct{} 47 | 48 | func init() { 49 | wrapperFuncSet = make(map[string]struct{}, len(WrapperFuncList)) 50 | for _, f := range WrapperFuncList { 51 | wrapperFuncSet[f] = struct{}{} 52 | } 53 | } 54 | 55 | func runAnalyze(pass *analysis.Pass) (interface{}, error) { 56 | r := newFileReader(pass.Fset) 57 | 58 | reportFunc := func(assignedAt, returnedAt token.Pos) { 59 | occPos := pass.Fset.Position(assignedAt) 60 | line := sprintInlineCode(r.GetLine(assignedAt)) 61 | pass.Reportf(returnedAt, "the error is assigned on L%d: %s", occPos.Line, line) 62 | } 63 | 64 | inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 65 | nodeFilter := []ast.Node{ 66 | (*ast.FuncDecl)(nil), 67 | } 68 | 69 | inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool { 70 | switch f := stack[0].(type) { 71 | case *ast.File: 72 | if isTestFile(pass.Fset, f) || isGeneratedFile(f) { 73 | return false 74 | } 75 | default: 76 | panic("unreachable") 77 | } 78 | NewChecker(pass.Fset, pass.TypesInfo, n.(*ast.FuncDecl)).Check(reportFunc) 79 | return false 80 | }) 81 | 82 | return nil, nil 83 | } 84 | -------------------------------------------------------------------------------- /analyzer_test.go: -------------------------------------------------------------------------------- 1 | package wraperr_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/tools/go/analysis/analysistest" 7 | 8 | "github.com/srvc/wraperr" 9 | ) 10 | 11 | func TestAnalyzer(t *testing.T) { 12 | testdata := analysistest.TestData() 13 | 14 | cases := []string{ 15 | "simple", 16 | "named_return", 17 | "multi_return", 18 | "multi_named_return", 19 | "select_stmt", 20 | "otherpkg_select_stmt", 21 | "generated", 22 | "clousure", 23 | } 24 | 25 | for _, tc := range cases { 26 | t.Run(tc, func(t *testing.T) { 27 | analysistest.Run(t, testdata, wraperr.Analyzer, tc) 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /checker.go: -------------------------------------------------------------------------------- 1 | package wraperr 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "go/types" 7 | "strings" 8 | ) 9 | 10 | type Checker interface { 11 | Check(ReportFunc) 12 | } 13 | 14 | type ReportFunc func(assignedAt, returnedAt token.Pos) 15 | 16 | func NewChecker( 17 | fset *token.FileSet, 18 | info *types.Info, 19 | f *ast.FuncDecl, 20 | ) Checker { 21 | return &checkerImpl{ 22 | fset: fset, 23 | info: info, 24 | funcType: f.Type, 25 | funcBody: f.Body, 26 | } 27 | } 28 | 29 | func newClousureChecker( 30 | fset *token.FileSet, 31 | info *types.Info, 32 | f *ast.FuncLit, 33 | ) Checker { 34 | return &checkerImpl{ 35 | fset: fset, 36 | info: info, 37 | funcType: f.Type, 38 | funcBody: f.Body, 39 | } 40 | } 41 | 42 | type checkerImpl struct { 43 | fset *token.FileSet 44 | info *types.Info 45 | funcType *ast.FuncType 46 | funcBody *ast.BlockStmt 47 | 48 | errInReturn []bool 49 | errNames map[string]struct{} 50 | errIdents map[string]*errIdent 51 | } 52 | 53 | func (c *checkerImpl) Check(report ReportFunc) { 54 | if !c.init() { 55 | return 56 | } 57 | 58 | ast.Inspect(c.funcBody, func(n ast.Node) bool { 59 | switch stmt := n.(type) { 60 | case *ast.AssignStmt: 61 | c.checkAssignment(stmt) 62 | case *ast.ReturnStmt: 63 | c.checkReturn(stmt, report) 64 | case *ast.FuncLit: 65 | newClousureChecker(c.fset, c.info, stmt).Check(report) 66 | return false 67 | } 68 | return true 69 | }) 70 | } 71 | 72 | func (c *checkerImpl) init() (ok bool) { 73 | if c.funcType == nil || c.funcType.Results == nil { 74 | return 75 | } 76 | 77 | fields := c.funcType.Results.List 78 | 79 | c.errInReturn = make([]bool, 0, 2*len(fields)) 80 | c.errNames = make(map[string]struct{}, 2*len(fields)) 81 | c.errIdents = map[string]*errIdent{} 82 | 83 | for _, f := range fields { 84 | isErr := isErrorType(c.info.TypeOf(f.Type)) 85 | ok = ok || isErr 86 | if len(f.Names) == 0 { 87 | c.errInReturn = append(c.errInReturn, isErr) 88 | } 89 | for _, n := range f.Names { 90 | c.errInReturn = append(c.errInReturn, isErr) 91 | c.errNames[n.Name] = struct{}{} 92 | } 93 | } 94 | 95 | return 96 | } 97 | 98 | func (c *checkerImpl) checkAssignment(stmt *ast.AssignStmt) { 99 | var errIds []*ast.Ident 100 | for _, expr := range stmt.Lhs { 101 | if !isErrorType(c.info.TypeOf(expr)) { 102 | continue 103 | } 104 | if id, ok := expr.(*ast.Ident); ok { 105 | errIds = append(errIds, id) 106 | } 107 | } 108 | if len(errIds) > 0 { 109 | // Detect wrapped error assignment 110 | var wrapped bool 111 | for _, expr := range stmt.Rhs { 112 | if cexpr, ok := expr.(*ast.CallExpr); ok { 113 | if c.isWrapped(cexpr) { 114 | wrapped = true 115 | } 116 | } 117 | } 118 | for _, id := range errIds { 119 | c.errIdents[id.Name] = &errIdent{Ident: id, wrapped: wrapped} 120 | } 121 | } 122 | } 123 | 124 | func (c *checkerImpl) checkReturn(stmt *ast.ReturnStmt, report ReportFunc) { 125 | switch len(stmt.Results) { 126 | case 0: 127 | // Named return values 128 | for n := range c.errNames { 129 | if errIdent, ok := c.errIdents[n]; ok && !errIdent.wrapped { 130 | report(errIdent.Pos(), stmt.Return) 131 | } 132 | } 133 | case len(c.errInReturn): 134 | // Simple return 135 | for i, expr := range stmt.Results { 136 | if !c.errInReturn[i] { 137 | continue 138 | } 139 | switch expr := expr.(type) { 140 | case *ast.Ident: 141 | if errIdent, ok := c.errIdents[expr.Name]; ok && !errIdent.wrapped { 142 | report(errIdent.Pos(), expr.NamePos) 143 | } 144 | case *ast.CallExpr: 145 | if !c.isWrapped(expr) { 146 | report(expr.Pos(), expr.Lparen) 147 | } 148 | default: 149 | // TODO: should report unexpected exper 150 | } 151 | } 152 | case 1: 153 | // Return another function directly 154 | switch expr := stmt.Results[0].(type) { 155 | case *ast.CallExpr: 156 | if !c.isWrapped(expr) { 157 | report(expr.Pos(), expr.Pos()) 158 | } 159 | default: 160 | // TODO: should report unexpected exper 161 | } 162 | default: 163 | // TODO: should report unexpected exper 164 | } 165 | } 166 | 167 | func (c *checkerImpl) isWrapped(call *ast.CallExpr) bool { 168 | switch fexpr := call.Fun.(type) { 169 | case *ast.SelectorExpr: 170 | if f, ok := c.info.ObjectOf(fexpr.Sel).(*types.Func); ok { 171 | chunks := []string{} 172 | for _, chunk := range strings.Split(f.FullName(), "/") { 173 | if chunk == "vendor" { 174 | chunks = []string{} 175 | } else { 176 | chunks = append(chunks, chunk) 177 | } 178 | } 179 | _, ok = wrapperFuncSet[strings.Join(chunks, "/")] 180 | return ok 181 | } 182 | } 183 | return false 184 | } 185 | -------------------------------------------------------------------------------- /cmd/wraperr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/srvc/wraperr" 5 | "golang.org/x/tools/go/analysis/singlechecker" 6 | ) 7 | 8 | func main() { 9 | singlechecker.Main(wraperr.Analyzer) 10 | } 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/srvc/wraperr 2 | 3 | require golang.org/x/tools v0.0.0-20181207222222-4c874b978acb 4 | 5 | go 1.11 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/tools v0.0.0-20181207222222-4c874b978acb h1:YIXCxYolAiiPmVSqA4gVUVcHo8Mi1ivU7ANnK9a63JY= 2 | golang.org/x/tools v0.0.0-20181207222222-4c874b978acb/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 3 | -------------------------------------------------------------------------------- /testdata/src/clousure/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | stderrors "errors" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func returnError() error { 10 | return stderrors.New("foobarbaz") 11 | } 12 | 13 | func returnError1() error { 14 | err := func() error { 15 | err := returnError() 16 | return err // want "L15: `err := returnError()" 17 | }() 18 | return err // want "L14: `err := func()" 19 | } 20 | 21 | func returnError2() error { 22 | err := func() error { 23 | err := returnError() 24 | return errors.WithStack(err) 25 | }() 26 | return err // want "L22: `err := func()" 27 | } 28 | 29 | func returnError3() error { 30 | err := func() error { 31 | err := returnError() 32 | return err // want "L31: `err := returnError()" 33 | }() 34 | return errors.WithStack(err) 35 | } 36 | 37 | func returnError4() error { 38 | err := func() error { 39 | err := returnError() 40 | return errors.WithStack(err) 41 | }() 42 | return errors.WithStack(err) 43 | } 44 | -------------------------------------------------------------------------------- /testdata/src/generated/main.go: -------------------------------------------------------------------------------- 1 | // Code generated by example.com/awesome-code-generator. DO NOT EDIT. 2 | 3 | package generated 4 | 5 | import ( 6 | stderrors "errors" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | func returnError1() error { 12 | return stderrors.New("foobarbaz") 13 | } 14 | 15 | func returnError2() error { 16 | return returnError1() 17 | } 18 | 19 | func returnError3() error { 20 | return errors.Wrap(returnError1(), "error occurred") 21 | } 22 | 23 | func returnError4() error { 24 | err := returnError1() 25 | return err 26 | } 27 | 28 | func returnError5() error { 29 | err := errors.WithStack(returnError1()) 30 | return err 31 | } 32 | 33 | func returnError6() error { 34 | err := returnError1() 35 | if err != nil { 36 | return err 37 | } 38 | return nil 39 | } 40 | 41 | func returnError7() error { 42 | err := returnError1() 43 | if err != nil { 44 | return errors.WithStack(err) 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /testdata/src/multi_named_return/main.go: -------------------------------------------------------------------------------- 1 | package multi_named_return 2 | 3 | import ( 4 | stderrors "errors" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func returnValueAndError1() (v string, err error) { 10 | return "quxquux", stderrors.New("foobarbaz") 11 | } 12 | 13 | func returnValueAndError2() (v string, err error) { 14 | v, err = returnValueAndError1() 15 | return // want "L14: `v, err = returnValueAndError1()" 16 | } 17 | 18 | func returnValueAndError3() (v string, err error) { 19 | v, err = returnValueAndError1() 20 | if err != nil { 21 | err = errors.Wrap(err, "error occurred") 22 | return 23 | } 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /testdata/src/multi_return/main.go: -------------------------------------------------------------------------------- 1 | package multi_return 2 | 3 | import ( 4 | stderrors "errors" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func returnValueAndError1() (string, error) { 10 | return "quxquux", stderrors.New("foobarbaz") 11 | } 12 | 13 | func returnValueAndError2() (string, error) { 14 | return returnValueAndError1() // want "L14: `return returnValueAndError1()" 15 | } 16 | 17 | func returnValueAndError3() (string, error) { 18 | v, err := returnValueAndError1() 19 | if err != nil { 20 | return "", err // want "L18: `v, err := returnValueAndError1()" 21 | } 22 | return v, nil 23 | } 24 | 25 | func returnValueAndError4() (string, error) { 26 | v, err := returnValueAndError1() 27 | if err != nil { 28 | return "", errors.Wrap(err, "error occurred") 29 | } 30 | return v, nil 31 | } 32 | -------------------------------------------------------------------------------- /testdata/src/named_return/main.go: -------------------------------------------------------------------------------- 1 | package named_return 2 | 3 | import ( 4 | stderrors "errors" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func returnError1() (err error) { 10 | err = stderrors.New("foobarbaz") 11 | return 12 | } 13 | 14 | func returnError2() (err error) { 15 | err = returnError1() 16 | return // want "L15: `err = returnError1()" 17 | } 18 | 19 | func returnError3() (err error) { 20 | err = errors.WithStack(returnError1()) 21 | return 22 | } 23 | 24 | func returnError4() (err error) { 25 | err = returnError1() 26 | err = errors.WithStack(err) 27 | return 28 | } 29 | 30 | func returnError5() (err error) { 31 | err = returnError1() 32 | if err != nil { 33 | err = errors.WithStack(err) 34 | } 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /testdata/src/otherpkg_select_stmt/func.go: -------------------------------------------------------------------------------- 1 | package otherpkg_select_stmt 2 | 3 | import ( 4 | "otherpkg_select_stmt/otherpkg" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func returnError1() error { 10 | return otherpkg.ReturnErrorFunc() // want "L9: `return otherpkg.ReturnErrorFunc()" 11 | } 12 | 13 | func returnError2() error { 14 | return errors.WithStack(otherpkg.ReturnErrorFunc()) 15 | } 16 | 17 | func returnError3() error { 18 | err := otherpkg.ReturnErrorFunc() 19 | return err // want "L18: `err := otherpkg.ReturnErrorFunc()" 20 | } 21 | 22 | func returnError4() error { 23 | err := otherpkg.ReturnErrorFunc() 24 | return errors.WithStack(err) 25 | } 26 | 27 | func returnError5() error { 28 | if err := otherpkg.ReturnErrorFunc(); err != nil { 29 | return err // want "L28: `if err := otherpkg.ReturnErrorFunc()" 30 | } 31 | return nil 32 | } 33 | 34 | func returnError6() error { 35 | if err := otherpkg.ReturnErrorFunc(); err != nil { 36 | return errors.WithStack(err) 37 | } 38 | return nil 39 | } 40 | 41 | func returnValueAndError1() (string, error) { 42 | return otherpkg.ReturnValueAndErrorFunc() // want "L42: `return otherpkg.ReturnValueAndErrorFunc()" 43 | } 44 | 45 | func returnValueAndError2() (string, error) { 46 | v, err := otherpkg.ReturnValueAndErrorFunc() 47 | return v, errors.WithStack(err) 48 | } 49 | 50 | func returnValueAndError3() (string, error) { 51 | if v, err := otherpkg.ReturnValueAndErrorFunc(); err == nil { 52 | return v, nil 53 | } else { 54 | return "", err // want "L51: `if v, err := otherpkg.ReturnValueAndErrorFunc()" 55 | } 56 | } 57 | 58 | func returnValueAndError4() (string, error) { 59 | if v, err := otherpkg.ReturnValueAndErrorFunc(); err == nil { 60 | return v, nil 61 | } else { 62 | return "", errors.WithStack(err) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /testdata/src/otherpkg_select_stmt/interface.go: -------------------------------------------------------------------------------- 1 | package otherpkg_select_stmt 2 | 3 | import ( 4 | "otherpkg_select_stmt/otherpkg" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func interfaceReturnError1() error { 10 | return otherpkg.InterfaceInstance.ReturnError() // want "L10: `return otherpkg.InterfaceInstance.ReturnError()" 11 | } 12 | 13 | func interfaceReturnError2() error { 14 | return errors.WithStack(otherpkg.InterfaceInstance.ReturnError()) 15 | } 16 | 17 | func interfaceReturnError3() error { 18 | err := otherpkg.InterfaceInstance.ReturnError() 19 | return err // want "L18: `err := otherpkg.InterfaceInstance.ReturnError()" 20 | } 21 | 22 | func interfaceReturnError4() error { 23 | err := otherpkg.InterfaceInstance.ReturnError() 24 | return errors.WithStack(err) 25 | } 26 | 27 | func interfaceReturnError5() error { 28 | if err := otherpkg.InterfaceInstance.ReturnError(); err != nil { 29 | return err // want "L28: `if err := otherpkg.InterfaceInstance.ReturnError()" 30 | } 31 | return nil 32 | } 33 | 34 | func interfaceReturnError6() error { 35 | if err := otherpkg.InterfaceInstance.ReturnError(); err != nil { 36 | return errors.WithStack(err) 37 | } 38 | return nil 39 | } 40 | 41 | func interfaceReturnValueAndError1() (string, error) { 42 | return otherpkg.InterfaceInstance.ReturnValueAndError() // want "L42: `return otherpkg.InterfaceInstance.ReturnValueAndError()" 43 | } 44 | 45 | func interfaceReturnValueAndError2() (string, error) { 46 | v, err := otherpkg.InterfaceInstance.ReturnValueAndError() 47 | return v, errors.WithStack(err) 48 | } 49 | 50 | func interfaceReturnValueAndError3() (string, error) { 51 | if v, err := otherpkg.InterfaceInstance.ReturnValueAndError(); err == nil { 52 | return v, nil 53 | } else { 54 | return "", err // want "L51: `if v, err := otherpkg.InterfaceInstance.ReturnValueAndError()" 55 | } 56 | } 57 | 58 | func interfaceReturnValueAndError4() (string, error) { 59 | if v, err := otherpkg.InterfaceInstance.ReturnValueAndError(); err == nil { 60 | return v, nil 61 | } else { 62 | return "", errors.WithStack(err) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /testdata/src/otherpkg_select_stmt/otherpkg/otherpkg.go: -------------------------------------------------------------------------------- 1 | package otherpkg 2 | 3 | func ReturnErrorFunc() error { 4 | return nil 5 | } 6 | 7 | func ReturnValueAndErrorFunc() (string, error) { 8 | return "", nil 9 | } 10 | 11 | type InterfaceType interface { 12 | ReturnError() error 13 | ReturnValueAndError() (string, error) 14 | } 15 | 16 | var ( 17 | InterfaceInstance InterfaceType 18 | StructInstance StructType 19 | ) 20 | 21 | type StructType struct { 22 | } 23 | 24 | func (s *StructType) ReturnError() error { 25 | return nil 26 | } 27 | 28 | func (s *StructType) ReturnValueAndError() (string, error) { 29 | return "", nil 30 | } 31 | -------------------------------------------------------------------------------- /testdata/src/otherpkg_select_stmt/struct.go: -------------------------------------------------------------------------------- 1 | package otherpkg_select_stmt 2 | 3 | import ( 4 | "otherpkg_select_stmt/otherpkg" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func structReturnError1() error { 10 | return otherpkg.StructInstance.ReturnError() // want "L10 `return otherpkg.StructInstance.ReturnError()" 11 | } 12 | 13 | func structReturnError2() error { 14 | return errors.WithStack(otherpkg.StructInstance.ReturnError()) 15 | } 16 | 17 | func structReturnError3() error { 18 | err := otherpkg.StructInstance.ReturnError() 19 | return err // want "L18: `err := otherpkg.StructInstance.ReturnError()" 20 | } 21 | 22 | func structReturnError4() error { 23 | err := otherpkg.StructInstance.ReturnError() 24 | return errors.WithStack(err) 25 | } 26 | 27 | func structReturnError5() error { 28 | if err := otherpkg.StructInstance.ReturnError(); err != nil { 29 | return err // want "L28: `if err := otherpkg.StructInstance.ReturnError()" 30 | } 31 | return nil 32 | } 33 | 34 | func structReturnError6() error { 35 | if err := otherpkg.StructInstance.ReturnError(); err != nil { 36 | return errors.WithStack(err) 37 | } 38 | return nil 39 | } 40 | 41 | func structReturnValueAndError1() (string, error) { 42 | return otherpkg.StructInstance.ReturnValueAndError() // want "L42: `return otherpkg.StructInstance.ReturnValueAndError()" 43 | } 44 | 45 | func structReturnValueAndError2() (string, error) { 46 | v, err := otherpkg.StructInstance.ReturnValueAndError() 47 | return v, errors.WithStack(err) 48 | } 49 | 50 | func structReturnValueAndError3() (string, error) { 51 | if v, err := otherpkg.StructInstance.ReturnValueAndError(); err == nil { 52 | return v, nil 53 | } else { 54 | return "", err // want "L51: `if v, err := otherpkg.StructInstance.ReturnValueAndError()" 55 | } 56 | } 57 | 58 | func structReturnValueAndError4() (string, error) { 59 | if v, err := otherpkg.StructInstance.ReturnValueAndError(); err == nil { 60 | return v, nil 61 | } else { 62 | return "", errors.WithStack(err) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /testdata/src/select_stmt/struct.go: -------------------------------------------------------------------------------- 1 | package select_stmt 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | type StructInstance struct { 8 | } 9 | 10 | func (s *StructInstance) ReturnError() error { 11 | return nil 12 | } 13 | 14 | func (s *StructInstance) ReturnError1() error { 15 | return s.ReturnError() // want "L15: `return s.ReturnError()" 16 | } 17 | 18 | func (s *StructInstance) ReturnError2() error { 19 | return errors.WithStack(s.ReturnError()) 20 | } 21 | 22 | func (s *StructInstance) ReturnError3() error { 23 | err := s.ReturnError() 24 | return err // want "L23: `err := s.ReturnError()" 25 | } 26 | 27 | func (s *StructInstance) ReturnError4() error { 28 | err := s.ReturnError() 29 | return errors.WithStack(err) 30 | } 31 | 32 | func (s *StructInstance) ReturnError5() error { 33 | if err := s.ReturnError(); err != nil { 34 | return err // want "L33: `if err := s.ReturnError()" 35 | } 36 | return nil 37 | } 38 | 39 | func (s *StructInstance) ReturnError6() error { 40 | if err := s.ReturnError(); err != nil { 41 | return errors.WithStack(err) 42 | } 43 | return nil 44 | } 45 | 46 | func (s *StructInstance) ReturnValueAndError1() (string, error) { 47 | return "", s.ReturnError() // want "L47: `return" 48 | } 49 | 50 | func (s *StructInstance) ReturnValueAndError2() (string, error) { 51 | return s.ReturnValueAndError1() // want "L51: `return s.ReturnValueAndError1()" 52 | } 53 | 54 | func (s *StructInstance) ReturnValueAndError3() (string, error) { 55 | v, err := s.ReturnValueAndError1() 56 | return v, errors.WithStack(err) 57 | } 58 | 59 | func (s *StructInstance) ReturnValueAndError4() (string, error) { 60 | if v, err := s.ReturnValueAndError1(); err == nil { 61 | return v, nil 62 | } else { 63 | return "", err // want "L60: `if v, err := s.ReturnValueAndError1()" 64 | } 65 | } 66 | 67 | func (s *StructInstance) ReturnValueAndError5() (string, error) { 68 | if v, err := s.ReturnValueAndError1(); err == nil { 69 | return v, nil 70 | } else { 71 | return "", errors.WithStack(err) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /testdata/src/simple/main.go: -------------------------------------------------------------------------------- 1 | package simple 2 | 3 | import ( 4 | stderrors "errors" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func returnError1() error { 10 | return stderrors.New("foobarbaz") 11 | } 12 | 13 | func returnError2() error { 14 | return returnError1() // want "L14: `return returnError1()" 15 | } 16 | 17 | func returnError3() error { 18 | return errors.Wrap(returnError1(), "error occurred") 19 | } 20 | 21 | func returnError4() error { 22 | err := returnError1() 23 | return err // want "L22: `err := returnError1()" 24 | } 25 | 26 | func returnError5() error { 27 | err := errors.WithStack(returnError1()) 28 | return err 29 | } 30 | 31 | func returnError6() error { 32 | err := returnError1() 33 | if err != nil { 34 | return err // want "L32: `err := returnError1()" 35 | } 36 | return nil 37 | } 38 | 39 | func returnError7() error { 40 | err := returnError1() 41 | if err != nil { 42 | return errors.WithStack(err) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /testdata/src/simple/main_test.go: -------------------------------------------------------------------------------- 1 | package simple 2 | 3 | func returnErrorInTest() error { 4 | return returnError1() 5 | } 6 | -------------------------------------------------------------------------------- /testdata/src/vendor/github.com/pkg/errors/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /testdata/src/vendor/github.com/pkg/errors/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: github.com/pkg/errors 3 | go: 4 | - 1.4.3 5 | - 1.5.4 6 | - 1.6.2 7 | - 1.7.1 8 | - tip 9 | 10 | script: 11 | - go test -v ./... 12 | -------------------------------------------------------------------------------- /testdata/src/vendor/github.com/pkg/errors/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /testdata/src/vendor/github.com/pkg/errors/README.md: -------------------------------------------------------------------------------- 1 | # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) 2 | 3 | Package errors provides simple error handling primitives. 4 | 5 | `go get github.com/pkg/errors` 6 | 7 | The traditional error handling idiom in Go is roughly akin to 8 | ```go 9 | if err != nil { 10 | return err 11 | } 12 | ``` 13 | which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. 14 | 15 | ## Adding context to an error 16 | 17 | The errors.Wrap function returns a new error that adds context to the original error. For example 18 | ```go 19 | _, err := ioutil.ReadAll(r) 20 | if err != nil { 21 | return errors.Wrap(err, "read failed") 22 | } 23 | ``` 24 | ## Retrieving the cause of an error 25 | 26 | Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. 27 | ```go 28 | type causer interface { 29 | Cause() error 30 | } 31 | ``` 32 | `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: 33 | ```go 34 | switch err := errors.Cause(err).(type) { 35 | case *MyError: 36 | // handle specifically 37 | default: 38 | // unknown error 39 | } 40 | ``` 41 | 42 | [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). 43 | 44 | ## Contributing 45 | 46 | We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. 47 | 48 | Before proposing a change, please discuss your change by raising an issue. 49 | 50 | ## Licence 51 | 52 | BSD-2-Clause 53 | -------------------------------------------------------------------------------- /testdata/src/vendor/github.com/pkg/errors/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: build-{build}.{branch} 2 | 3 | clone_folder: C:\gopath\src\github.com\pkg\errors 4 | shallow_clone: true # for startup speed 5 | 6 | environment: 7 | GOPATH: C:\gopath 8 | 9 | platform: 10 | - x64 11 | 12 | # http://www.appveyor.com/docs/installed-software 13 | install: 14 | # some helpful output for debugging builds 15 | - go version 16 | - go env 17 | # pre-installed MinGW at C:\MinGW is 32bit only 18 | # but MSYS2 at C:\msys64 has mingw64 19 | - set PATH=C:\msys64\mingw64\bin;%PATH% 20 | - gcc --version 21 | - g++ --version 22 | 23 | build_script: 24 | - go install -v ./... 25 | 26 | test_script: 27 | - set PATH=C:\gopath\bin;%PATH% 28 | - go test -v ./... 29 | 30 | #artifacts: 31 | # - path: '%GOPATH%\bin\*.exe' 32 | deploy: off 33 | -------------------------------------------------------------------------------- /testdata/src/vendor/github.com/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Package errors provides simple error handling primitives. 2 | // 3 | // The traditional error handling idiom in Go is roughly akin to 4 | // 5 | // if err != nil { 6 | // return err 7 | // } 8 | // 9 | // which applied recursively up the call stack results in error reports 10 | // without context or debugging information. The errors package allows 11 | // programmers to add context to the failure path in their code in a way 12 | // that does not destroy the original value of the error. 13 | // 14 | // Adding context to an error 15 | // 16 | // The errors.Wrap function returns a new error that adds context to the 17 | // original error by recording a stack trace at the point Wrap is called, 18 | // and the supplied message. For example 19 | // 20 | // _, err := ioutil.ReadAll(r) 21 | // if err != nil { 22 | // return errors.Wrap(err, "read failed") 23 | // } 24 | // 25 | // If additional control is required the errors.WithStack and errors.WithMessage 26 | // functions destructure errors.Wrap into its component operations of annotating 27 | // an error with a stack trace and an a message, respectively. 28 | // 29 | // Retrieving the cause of an error 30 | // 31 | // Using errors.Wrap constructs a stack of errors, adding context to the 32 | // preceding error. Depending on the nature of the error it may be necessary 33 | // to reverse the operation of errors.Wrap to retrieve the original error 34 | // for inspection. Any error value which implements this interface 35 | // 36 | // type causer interface { 37 | // Cause() error 38 | // } 39 | // 40 | // can be inspected by errors.Cause. errors.Cause will recursively retrieve 41 | // the topmost error which does not implement causer, which is assumed to be 42 | // the original cause. For example: 43 | // 44 | // switch err := errors.Cause(err).(type) { 45 | // case *MyError: 46 | // // handle specifically 47 | // default: 48 | // // unknown error 49 | // } 50 | // 51 | // causer interface is not exported by this package, but is considered a part 52 | // of stable public API. 53 | // 54 | // Formatted printing of errors 55 | // 56 | // All error values returned from this package implement fmt.Formatter and can 57 | // be formatted by the fmt package. The following verbs are supported 58 | // 59 | // %s print the error. If the error has a Cause it will be 60 | // printed recursively 61 | // %v see %s 62 | // %+v extended format. Each Frame of the error's StackTrace will 63 | // be printed in detail. 64 | // 65 | // Retrieving the stack trace of an error or wrapper 66 | // 67 | // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are 68 | // invoked. This information can be retrieved with the following interface. 69 | // 70 | // type stackTracer interface { 71 | // StackTrace() errors.StackTrace 72 | // } 73 | // 74 | // Where errors.StackTrace is defined as 75 | // 76 | // type StackTrace []Frame 77 | // 78 | // The Frame type represents a call site in the stack trace. Frame supports 79 | // the fmt.Formatter interface that can be used for printing information about 80 | // the stack trace of this error. For example: 81 | // 82 | // if err, ok := err.(stackTracer); ok { 83 | // for _, f := range err.StackTrace() { 84 | // fmt.Printf("%+s:%d", f) 85 | // } 86 | // } 87 | // 88 | // stackTracer interface is not exported by this package, but is considered a part 89 | // of stable public API. 90 | // 91 | // See the documentation for Frame.Format for more details. 92 | package errors 93 | 94 | import ( 95 | "fmt" 96 | "io" 97 | ) 98 | 99 | // New returns an error with the supplied message. 100 | // New also records the stack trace at the point it was called. 101 | func New(message string) error { 102 | return &fundamental{ 103 | msg: message, 104 | stack: callers(), 105 | } 106 | } 107 | 108 | // Errorf formats according to a format specifier and returns the string 109 | // as a value that satisfies error. 110 | // Errorf also records the stack trace at the point it was called. 111 | func Errorf(format string, args ...interface{}) error { 112 | return &fundamental{ 113 | msg: fmt.Sprintf(format, args...), 114 | stack: callers(), 115 | } 116 | } 117 | 118 | // fundamental is an error that has a message and a stack, but no caller. 119 | type fundamental struct { 120 | msg string 121 | *stack 122 | } 123 | 124 | func (f *fundamental) Error() string { return f.msg } 125 | 126 | func (f *fundamental) Format(s fmt.State, verb rune) { 127 | switch verb { 128 | case 'v': 129 | if s.Flag('+') { 130 | io.WriteString(s, f.msg) 131 | f.stack.Format(s, verb) 132 | return 133 | } 134 | fallthrough 135 | case 's': 136 | io.WriteString(s, f.msg) 137 | case 'q': 138 | fmt.Fprintf(s, "%q", f.msg) 139 | } 140 | } 141 | 142 | // WithStack annotates err with a stack trace at the point WithStack was called. 143 | // If err is nil, WithStack returns nil. 144 | func WithStack(err error) error { 145 | if err == nil { 146 | return nil 147 | } 148 | return &withStack{ 149 | err, 150 | callers(), 151 | } 152 | } 153 | 154 | type withStack struct { 155 | error 156 | *stack 157 | } 158 | 159 | func (w *withStack) Cause() error { return w.error } 160 | 161 | func (w *withStack) Format(s fmt.State, verb rune) { 162 | switch verb { 163 | case 'v': 164 | if s.Flag('+') { 165 | fmt.Fprintf(s, "%+v", w.Cause()) 166 | w.stack.Format(s, verb) 167 | return 168 | } 169 | fallthrough 170 | case 's': 171 | io.WriteString(s, w.Error()) 172 | case 'q': 173 | fmt.Fprintf(s, "%q", w.Error()) 174 | } 175 | } 176 | 177 | // Wrap returns an error annotating err with a stack trace 178 | // at the point Wrap is called, and the supplied message. 179 | // If err is nil, Wrap returns nil. 180 | func Wrap(err error, message string) error { 181 | if err == nil { 182 | return nil 183 | } 184 | err = &withMessage{ 185 | cause: err, 186 | msg: message, 187 | } 188 | return &withStack{ 189 | err, 190 | callers(), 191 | } 192 | } 193 | 194 | // Wrapf returns an error annotating err with a stack trace 195 | // at the point Wrapf is call, and the format specifier. 196 | // If err is nil, Wrapf returns nil. 197 | func Wrapf(err error, format string, args ...interface{}) error { 198 | if err == nil { 199 | return nil 200 | } 201 | err = &withMessage{ 202 | cause: err, 203 | msg: fmt.Sprintf(format, args...), 204 | } 205 | return &withStack{ 206 | err, 207 | callers(), 208 | } 209 | } 210 | 211 | // WithMessage annotates err with a new message. 212 | // If err is nil, WithMessage returns nil. 213 | func WithMessage(err error, message string) error { 214 | if err == nil { 215 | return nil 216 | } 217 | return &withMessage{ 218 | cause: err, 219 | msg: message, 220 | } 221 | } 222 | 223 | type withMessage struct { 224 | cause error 225 | msg string 226 | } 227 | 228 | func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } 229 | func (w *withMessage) Cause() error { return w.cause } 230 | 231 | func (w *withMessage) Format(s fmt.State, verb rune) { 232 | switch verb { 233 | case 'v': 234 | if s.Flag('+') { 235 | fmt.Fprintf(s, "%+v\n", w.Cause()) 236 | io.WriteString(s, w.msg) 237 | return 238 | } 239 | fallthrough 240 | case 's', 'q': 241 | io.WriteString(s, w.Error()) 242 | } 243 | } 244 | 245 | // Cause returns the underlying cause of the error, if possible. 246 | // An error value has a cause if it implements the following 247 | // interface: 248 | // 249 | // type causer interface { 250 | // Cause() error 251 | // } 252 | // 253 | // If the error does not implement Cause, the original error will 254 | // be returned. If the error is nil, nil will be returned without further 255 | // investigation. 256 | func Cause(err error) error { 257 | type causer interface { 258 | Cause() error 259 | } 260 | 261 | for err != nil { 262 | cause, ok := err.(causer) 263 | if !ok { 264 | break 265 | } 266 | err = cause.Cause() 267 | } 268 | return err 269 | } 270 | -------------------------------------------------------------------------------- /testdata/src/vendor/github.com/pkg/errors/stack.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "path" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | // Frame represents a program counter inside a stack frame. 12 | type Frame uintptr 13 | 14 | // pc returns the program counter for this frame; 15 | // multiple frames may have the same PC value. 16 | func (f Frame) pc() uintptr { return uintptr(f) - 1 } 17 | 18 | // file returns the full path to the file that contains the 19 | // function for this Frame's pc. 20 | func (f Frame) file() string { 21 | fn := runtime.FuncForPC(f.pc()) 22 | if fn == nil { 23 | return "unknown" 24 | } 25 | file, _ := fn.FileLine(f.pc()) 26 | return file 27 | } 28 | 29 | // line returns the line number of source code of the 30 | // function for this Frame's pc. 31 | func (f Frame) line() int { 32 | fn := runtime.FuncForPC(f.pc()) 33 | if fn == nil { 34 | return 0 35 | } 36 | _, line := fn.FileLine(f.pc()) 37 | return line 38 | } 39 | 40 | // Format formats the frame according to the fmt.Formatter interface. 41 | // 42 | // %s source file 43 | // %d source line 44 | // %n function name 45 | // %v equivalent to %s:%d 46 | // 47 | // Format accepts flags that alter the printing of some verbs, as follows: 48 | // 49 | // %+s path of source file relative to the compile time GOPATH 50 | // %+v equivalent to %+s:%d 51 | func (f Frame) Format(s fmt.State, verb rune) { 52 | switch verb { 53 | case 's': 54 | switch { 55 | case s.Flag('+'): 56 | pc := f.pc() 57 | fn := runtime.FuncForPC(pc) 58 | if fn == nil { 59 | io.WriteString(s, "unknown") 60 | } else { 61 | file, _ := fn.FileLine(pc) 62 | fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) 63 | } 64 | default: 65 | io.WriteString(s, path.Base(f.file())) 66 | } 67 | case 'd': 68 | fmt.Fprintf(s, "%d", f.line()) 69 | case 'n': 70 | name := runtime.FuncForPC(f.pc()).Name() 71 | io.WriteString(s, funcname(name)) 72 | case 'v': 73 | f.Format(s, 's') 74 | io.WriteString(s, ":") 75 | f.Format(s, 'd') 76 | } 77 | } 78 | 79 | // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). 80 | type StackTrace []Frame 81 | 82 | func (st StackTrace) Format(s fmt.State, verb rune) { 83 | switch verb { 84 | case 'v': 85 | switch { 86 | case s.Flag('+'): 87 | for _, f := range st { 88 | fmt.Fprintf(s, "\n%+v", f) 89 | } 90 | case s.Flag('#'): 91 | fmt.Fprintf(s, "%#v", []Frame(st)) 92 | default: 93 | fmt.Fprintf(s, "%v", []Frame(st)) 94 | } 95 | case 's': 96 | fmt.Fprintf(s, "%s", []Frame(st)) 97 | } 98 | } 99 | 100 | // stack represents a stack of program counters. 101 | type stack []uintptr 102 | 103 | func (s *stack) Format(st fmt.State, verb rune) { 104 | switch verb { 105 | case 'v': 106 | switch { 107 | case st.Flag('+'): 108 | for _, pc := range *s { 109 | f := Frame(pc) 110 | fmt.Fprintf(st, "\n%+v", f) 111 | } 112 | } 113 | } 114 | } 115 | 116 | func (s *stack) StackTrace() StackTrace { 117 | f := make([]Frame, len(*s)) 118 | for i := 0; i < len(f); i++ { 119 | f[i] = Frame((*s)[i]) 120 | } 121 | return f 122 | } 123 | 124 | func callers() *stack { 125 | const depth = 32 126 | var pcs [depth]uintptr 127 | n := runtime.Callers(3, pcs[:]) 128 | var st stack = pcs[0:n] 129 | return &st 130 | } 131 | 132 | // funcname removes the path prefix component of a function's name reported by func.Name(). 133 | func funcname(name string) string { 134 | i := strings.LastIndex(name, "/") 135 | name = name[i+1:] 136 | i = strings.Index(name, ".") 137 | return name[i+1:] 138 | } 139 | 140 | func trimGOPATH(name, file string) string { 141 | // Here we want to get the source file path relative to the compile time 142 | // GOPATH. As of Go 1.6.x there is no direct way to know the compiled 143 | // GOPATH at runtime, but we can infer the number of path segments in the 144 | // GOPATH. We note that fn.Name() returns the function name qualified by 145 | // the import path, which does not include the GOPATH. Thus we can trim 146 | // segments from the beginning of the file path until the number of path 147 | // separators remaining is one more than the number of path separators in 148 | // the function name. For example, given: 149 | // 150 | // GOPATH /home/user 151 | // file /home/user/src/pkg/sub/file.go 152 | // fn.Name() pkg/sub.Type.Method 153 | // 154 | // We want to produce: 155 | // 156 | // pkg/sub/file.go 157 | // 158 | // From this we can easily see that fn.Name() has one less path separator 159 | // than our desired output. We count separators from the end of the file 160 | // path until it finds two more than in the function name and then move 161 | // one character forward to preserve the initial path segment without a 162 | // leading separator. 163 | const sep = "/" 164 | goal := strings.Count(name, sep) + 2 165 | i := len(file) 166 | for n := 0; n < goal; n++ { 167 | i = strings.LastIndex(file[:i], sep) 168 | if i == -1 { 169 | // not enough separators found, set i so that the slice expression 170 | // below leaves file unmodified 171 | i = -len(sep) 172 | break 173 | } 174 | } 175 | // get back to 0 or trim the leading separator 176 | file = file[i+len(sep):] 177 | return file 178 | } 179 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package wraperr 2 | 3 | import ( 4 | "bufio" 5 | "go/ast" 6 | "go/token" 7 | "go/types" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | genHdr = "// Code generated " 14 | genFtr = " DO NOT EDIT." 15 | 16 | errorType *types.Interface 17 | ) 18 | 19 | func init() { 20 | errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) 21 | } 22 | 23 | func isErrorType(t types.Type) bool { 24 | return t != nil && types.Implements(t, errorType) 25 | } 26 | 27 | // https://golang.org/s/generatedcode 28 | func isGeneratedFile(f *ast.File) bool { 29 | for _, cg := range f.Comments { 30 | for _, c := range cg.List { 31 | src := c.Text 32 | // from https://github.com/golang/lint/blob/06c8688daad7faa9da5a0c2f163a3d14aac986ca/lint.go#L129 33 | if strings.HasPrefix(src, genHdr) && strings.HasSuffix(src, genFtr) && len(src) >= len(genHdr)+len(genFtr) { 34 | return true 35 | } 36 | } 37 | } 38 | return false 39 | } 40 | 41 | func isTestFile(fset *token.FileSet, f *ast.File) bool { 42 | return strings.HasSuffix(fset.File(f.Pos()).Name(), "_test.go") 43 | } 44 | 45 | func sprintInlineCode(s string) string { 46 | cc := 1 47 | c := cc 48 | for _, r := range s { 49 | if r == '`' { 50 | cc++ 51 | if cc > c { 52 | c = cc 53 | } 54 | } else { 55 | cc = 1 56 | } 57 | } 58 | q := strings.Repeat("`", c) 59 | return q + s + q 60 | } 61 | 62 | type fileReader struct { 63 | fset *token.FileSet 64 | lines map[string][]string 65 | } 66 | 67 | func newFileReader(fset *token.FileSet) *fileReader { 68 | return &fileReader{ 69 | fset: fset, 70 | lines: make(map[string][]string), 71 | } 72 | } 73 | 74 | // ref: https://github.com/kisielk/errcheck/blob/1787c4bee836470bf45018cfbc783650db3c6501/internal/errcheck/errcheck.go#L488-L498 75 | func (r *fileReader) GetLine(tp token.Pos) string { 76 | pos := r.fset.Position(tp) 77 | foundLines, ok := r.lines[pos.Filename] 78 | 79 | if !ok { 80 | f, err := os.Open(pos.Filename) 81 | if err == nil { 82 | sc := bufio.NewScanner(f) 83 | for sc.Scan() { 84 | foundLines = append(foundLines, sc.Text()) 85 | } 86 | r.lines[pos.Filename] = foundLines 87 | f.Close() 88 | } 89 | } 90 | 91 | line := "??" 92 | if pos.Line-1 < len(foundLines) { 93 | line = strings.TrimSpace(foundLines[pos.Line-1]) 94 | } 95 | 96 | return line 97 | } 98 | --------------------------------------------------------------------------------