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 }
80