src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa1032.go (2056B)


      1 package sa1032
      2 
      3 import (
      4 	"honnef.co/go/tools/analysis/callcheck"
      5 	"honnef.co/go/tools/analysis/lint"
      6 	"honnef.co/go/tools/go/ir"
      7 	"honnef.co/go/tools/internal/passes/buildir"
      8 
      9 	"golang.org/x/tools/go/analysis"
     10 )
     11 
     12 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     13 	Analyzer: &analysis.Analyzer{
     14 		Name:     "SA1032",
     15 		Requires: []*analysis.Analyzer{buildir.Analyzer},
     16 		Run:      callcheck.Analyzer(rules),
     17 	},
     18 	Doc: &lint.RawDocumentation{
     19 		Title: `Wrong order of arguments to \'errors.Is\'`,
     20 		Text: `
     21 The first argument of the function \'errors.Is\' is the error
     22 that we have and the second argument is the error we're trying to match against.
     23 For example:
     24 
     25 	if errors.Is(err, io.EOF) { ... }
     26 
     27 This check detects some cases where the two arguments have been swapped. It
     28 flags any calls where the first argument is referring to a package-level error
     29 variable, such as
     30 
     31 	if errors.Is(io.EOF, err) { /* this is wrong */ }`,
     32 		Since:    "2024.1",
     33 		Severity: lint.SeverityError,
     34 		MergeIf:  lint.MergeIfAny,
     35 	},
     36 })
     37 
     38 var Analyzer = SCAnalyzer.Analyzer
     39 
     40 var rules = map[string]callcheck.Check{
     41 	"errors.Is": validateIs,
     42 }
     43 
     44 func validateIs(call *callcheck.Call) {
     45 	if len(call.Args) != 2 {
     46 		return
     47 	}
     48 
     49 	global := func(arg *callcheck.Argument) *ir.Global {
     50 		v, ok := arg.Value.Value.(*ir.Load)
     51 		if !ok {
     52 			return nil
     53 		}
     54 		g, _ := v.X.(*ir.Global)
     55 		return g
     56 	}
     57 
     58 	x, y := call.Args[0], call.Args[1]
     59 	gx := global(x)
     60 	if gx == nil {
     61 		return
     62 	}
     63 
     64 	if pkgx := gx.Package().Pkg; pkgx != nil && pkgx.Path() != call.Pass.Pkg.Path() {
     65 		// x is a global that's not in this package
     66 
     67 		if gy := global(y); gy != nil {
     68 			if pkgy := gy.Package().Pkg; pkgy != nil && pkgy.Path() != call.Pass.Pkg.Path() {
     69 				// Both arguments refer to globals that aren't in this package. This can
     70 				// genuinely happen for external tests that check that one error "is"
     71 				// another one. net/http's external tests, for example, do
     72 				// `errors.Is(http.ErrNotSupported, errors.ErrUnsupported)`.
     73 				return
     74 			}
     75 		}
     76 
     77 		call.Invalid("arguments have the wrong order")
     78 	}
     79 }