github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/golinters/errs/main.go (about) 1 // Copyright (C) 2019 Storj Labs, Inc. 2 // See LICENSE for copying information. 3 4 package errs 5 6 import ( 7 "github.com/elek/golangci-lint/pkg/golinters/goanalysis" 8 "go/ast" 9 "go/token" 10 11 "golang.org/x/tools/go/analysis" 12 "golang.org/x/tools/go/analysis/passes/inspect" 13 "golang.org/x/tools/go/ast/inspector" 14 "golang.org/x/tools/go/types/typeutil" 15 ) 16 17 func NewErrsCheck() *goanalysis.Linter { 18 return goanalysis.NewLinter( 19 "errs", 20 "STORJ: moknit related checks", 21 []*analysis.Analyzer{ 22 Analyzer, 23 }, 24 nil, 25 ).WithLoadMode(goanalysis.LoadModeWholeProgram) 26 } 27 28 // Analyzer verifies whether errs package is properly used. 29 var Analyzer = &analysis.Analyzer{ 30 Name: "errs", 31 Doc: "check for proper usage of errs package", 32 Run: run, 33 Requires: []*analysis.Analyzer{ 34 inspect.Analyzer, 35 }, 36 FactTypes: []analysis.Fact{}, 37 } 38 39 func run(pass *analysis.Pass) (interface{}, error) { 40 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 41 nodeFilter := []ast.Node{ 42 (*ast.CallExpr)(nil), 43 } 44 inspect.Preorder(nodeFilter, func(n ast.Node) { 45 call := n.(*ast.CallExpr) 46 fn := typeutil.StaticCallee(pass.TypesInfo, call) 47 if fn == nil { 48 return // not a static call 49 } 50 51 switch fn.FullName() { 52 case "github.com/zeebo/errs.Combine": 53 if len(call.Args) == 0 { 54 pass.Reportf(call.Lparen, "errs.Combine() can be simplified to nil") 55 } 56 if len(call.Args) == 1 && call.Ellipsis == token.NoPos { 57 pass.Reportf(call.Lparen, "errs.Combine(x) can be simplified to x") 58 } 59 case "(*github.com/zeebo/errs.Class).New": 60 if len(call.Args) == 0 { 61 return 62 } 63 // Disallow things like Error.New(err.Error()) 64 65 switch arg := call.Args[0].(type) { 66 case *ast.BasicLit: // allow string constants 67 case *ast.Ident: // allow string variables 68 default: 69 // allow "alpha" + "beta" + "gamma" 70 if IsConcatString(arg) { 71 return 72 } 73 74 pass.Reportf(call.Lparen, "(*errs.Class).New with non-obvious format string") 75 } 76 } 77 }) 78 79 return nil, nil 80 } 81 82 // IsConcatString returns whether arg is a basic string expression. 83 func IsConcatString(arg ast.Expr) bool { 84 switch arg := arg.(type) { 85 case *ast.BasicLit: 86 return arg.Kind == token.STRING 87 case *ast.BinaryExpr: 88 return arg.Op == token.ADD && IsConcatString(arg.X) && IsConcatString(arg.Y) 89 default: 90 return false 91 } 92 }