aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/polyfloyd
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2021-02-22 20:37:25 +0100
committerDmitry Vyukov <dvyukov@google.com>2021-02-22 21:02:12 +0100
commitfcc6d71be2c3ce7d9305c04fc2e87af554571bac (patch)
treeb01dbb3d1e2988e28ea158d2d543d603ec0b9569 /vendor/github.com/polyfloyd
parent8f23c528ad5a943b9ffec5dcaf332fd0f614006e (diff)
go.mod: update golangci-lint to v1.37
Diffstat (limited to 'vendor/github.com/polyfloyd')
-rw-r--r--vendor/github.com/polyfloyd/go-errorlint/LICENSE21
-rw-r--r--vendor/github.com/polyfloyd/go-errorlint/errorlint/analysis.go44
-rw-r--r--vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go245
3 files changed, 310 insertions, 0 deletions
diff --git a/vendor/github.com/polyfloyd/go-errorlint/LICENSE b/vendor/github.com/polyfloyd/go-errorlint/LICENSE
new file mode 100644
index 000000000..b7f88cf1c
--- /dev/null
+++ b/vendor/github.com/polyfloyd/go-errorlint/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2019 polyfloyd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/polyfloyd/go-errorlint/errorlint/analysis.go b/vendor/github.com/polyfloyd/go-errorlint/errorlint/analysis.go
new file mode 100644
index 000000000..20d1c1e7c
--- /dev/null
+++ b/vendor/github.com/polyfloyd/go-errorlint/errorlint/analysis.go
@@ -0,0 +1,44 @@
+package errorlint
+
+import (
+ "flag"
+ "sort"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+func NewAnalyzer() *analysis.Analyzer {
+ return &analysis.Analyzer{
+ Name: "errorlint",
+ Doc: "Source code linter for Go software that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.",
+ Run: run,
+ Flags: flagSet,
+ }
+}
+
+var (
+ flagSet flag.FlagSet
+ checkErrorf bool
+)
+
+func init() {
+ flagSet.BoolVar(&checkErrorf, "errorf", false, "Check whether fmt.Errorf uses the %w verb for formatting errors. See the readme for caveats")
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+ lints := []Lint{}
+ if checkErrorf {
+ l := LintFmtErrorfCalls(pass.Fset, *pass.TypesInfo)
+ lints = append(lints, l...)
+ }
+ l := LintErrorComparisons(pass.Fset, *pass.TypesInfo)
+ lints = append(lints, l...)
+ l = LintErrorTypeAssertions(pass.Fset, *pass.TypesInfo)
+ lints = append(lints, l...)
+ sort.Sort(ByPosition(lints))
+
+ for _, l := range lints {
+ pass.Report(analysis.Diagnostic{Pos: l.Pos, Message: l.Message})
+ }
+ return nil, nil
+}
diff --git a/vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go b/vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go
new file mode 100644
index 000000000..e9f889c93
--- /dev/null
+++ b/vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go
@@ -0,0 +1,245 @@
+package errorlint
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "regexp"
+)
+
+type Lint struct {
+ Message string
+ Pos token.Pos
+}
+
+type ByPosition []Lint
+
+func (l ByPosition) Len() int { return len(l) }
+func (l ByPosition) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+func (l ByPosition) Less(i, j int) bool {
+ return l[i].Pos < l[j].Pos
+}
+
+func LintFmtErrorfCalls(fset *token.FileSet, info types.Info) []Lint {
+ lints := []Lint{}
+ for expr, t := range info.Types {
+ // Search for error expressions that are the result of fmt.Errorf
+ // invocations.
+ if t.Type.String() != "error" {
+ continue
+ }
+ call, ok := isFmtErrorfCallExpr(info, expr)
+ if !ok {
+ continue
+ }
+
+ // Find all % fields in the format string.
+ formatVerbs, ok := printfFormatStringVerbs(info, call)
+ if !ok {
+ continue
+ }
+
+ // For any arguments that are errors, check whether the wrapping verb
+ // is used. Only one %w verb may be used in a single format string at a
+ // time, so we stop after finding a correct %w.
+ var lintArg ast.Expr
+ args := call.Args[1:]
+ for i := 0; i < len(args) && i < len(formatVerbs); i++ {
+ if info.Types[args[i]].Type.String() != "error" && !isErrorStringCall(info, args[i]) {
+ continue
+ }
+
+ if formatVerbs[i] == "%w" {
+ lintArg = nil
+ break
+ }
+
+ if lintArg == nil {
+ lintArg = args[i]
+ }
+ }
+ if lintArg != nil {
+ lints = append(lints, Lint{
+ Message: "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors",
+ Pos: lintArg.Pos(),
+ })
+ }
+ }
+ return lints
+}
+
+// isErrorStringCall tests whether the expression is a string expression that
+// is the result of an `(error).Error()` method call.
+func isErrorStringCall(info types.Info, expr ast.Expr) bool {
+ if info.Types[expr].Type.String() == "string" {
+ if call, ok := expr.(*ast.CallExpr); ok {
+ if callSel, ok := call.Fun.(*ast.SelectorExpr); ok {
+ fun := info.Uses[callSel.Sel].(*types.Func)
+ return fun.Type().String() == "func() string" && fun.Name() == "Error"
+ }
+ }
+ }
+ return false
+}
+
+func printfFormatStringVerbs(info types.Info, call *ast.CallExpr) ([]string, bool) {
+ if len(call.Args) <= 1 {
+ return nil, false
+ }
+ strLit, ok := call.Args[0].(*ast.BasicLit)
+ if !ok {
+ // Ignore format strings that are not literals.
+ return nil, false
+ }
+ formatString := constant.StringVal(info.Types[strLit].Value)
+
+ // Naive format string argument verb. This does not take modifiers such as
+ // padding into account...
+ re := regexp.MustCompile(`%[^%]`)
+ return re.FindAllString(formatString, -1), true
+}
+
+func isFmtErrorfCallExpr(info types.Info, expr ast.Expr) (*ast.CallExpr, bool) {
+ call, ok := expr.(*ast.CallExpr)
+ if !ok {
+ return nil, false
+ }
+ fn, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ // TODO: Support fmt.Errorf variable aliases?
+ return nil, false
+ }
+ obj := info.Uses[fn.Sel]
+
+ pkg := obj.Pkg()
+ if pkg != nil && pkg.Name() == "fmt" && obj.Name() == "Errorf" {
+ return call, true
+ }
+ return nil, false
+}
+
+func LintErrorComparisons(fset *token.FileSet, info types.Info) []Lint {
+ lints := []Lint{}
+
+ for expr := range info.Types {
+ // Find == and != operations.
+ binExpr, ok := expr.(*ast.BinaryExpr)
+ if !ok {
+ continue
+ }
+ if binExpr.Op != token.EQL && binExpr.Op != token.NEQ {
+ continue
+ }
+ // Comparing errors with nil is okay.
+ if isNilComparison(binExpr) {
+ continue
+ }
+ // Find comparisons of which one side is a of type error.
+ if !isErrorComparison(info, binExpr) {
+ continue
+ }
+
+ lints = append(lints, Lint{
+ Message: fmt.Sprintf("comparing with %s will fail on wrapped errors. Use errors.Is to check for a specific error", binExpr.Op),
+ Pos: binExpr.Pos(),
+ })
+ }
+
+ for scope := range info.Scopes {
+ // Find value switch blocks.
+ switchStmt, ok := scope.(*ast.SwitchStmt)
+ if !ok {
+ continue
+ }
+ // Check whether the switch operates on an error type.
+ if switchStmt.Tag == nil {
+ continue
+ }
+ tagType := info.Types[switchStmt.Tag]
+ if tagType.Type.String() != "error" {
+ continue
+ }
+
+ lints = append(lints, Lint{
+ Message: "switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors",
+ Pos: switchStmt.Pos(),
+ })
+ }
+
+ return lints
+}
+
+func isNilComparison(binExpr *ast.BinaryExpr) bool {
+ if ident, ok := binExpr.X.(*ast.Ident); ok && ident.Name == "nil" {
+ return true
+ }
+ if ident, ok := binExpr.Y.(*ast.Ident); ok && ident.Name == "nil" {
+ return true
+ }
+ return false
+}
+
+func isErrorComparison(info types.Info, binExpr *ast.BinaryExpr) bool {
+ tx := info.Types[binExpr.X]
+ ty := info.Types[binExpr.Y]
+ return tx.Type.String() == "error" || ty.Type.String() == "error"
+}
+
+func LintErrorTypeAssertions(fset *token.FileSet, info types.Info) []Lint {
+ lints := []Lint{}
+
+ for expr := range info.Types {
+ // Find type assertions.
+ typeAssert, ok := expr.(*ast.TypeAssertExpr)
+ if !ok {
+ continue
+ }
+
+ // Find type assertions that operate on values of type error.
+ if !isErrorTypeAssertion(info, typeAssert) {
+ continue
+ }
+
+ lints = append(lints, Lint{
+ Message: "type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors",
+ Pos: typeAssert.Pos(),
+ })
+ }
+
+ for scope := range info.Scopes {
+ // Find type switches.
+ typeSwitch, ok := scope.(*ast.TypeSwitchStmt)
+ if !ok {
+ continue
+ }
+
+ // Find the type assertion in the type switch.
+ var typeAssert *ast.TypeAssertExpr
+ switch t := typeSwitch.Assign.(type) {
+ case *ast.ExprStmt:
+ typeAssert = t.X.(*ast.TypeAssertExpr)
+ case *ast.AssignStmt:
+ typeAssert = t.Rhs[0].(*ast.TypeAssertExpr)
+ }
+
+ // Check whether the type switch is on a value of type error.
+ if !isErrorTypeAssertion(info, typeAssert) {
+ continue
+ }
+
+ lints = append(lints, Lint{
+ Message: "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors",
+ Pos: typeAssert.Pos(),
+ })
+ }
+
+ return lints
+}
+
+func isErrorTypeAssertion(info types.Info, typeAssert *ast.TypeAssertExpr) bool {
+ t := info.Types[typeAssert.X]
+ return t.Type.String() == "error"
+}