80 lines
2.0 KiB
Go
80 lines
2.0 KiB
Go
package sa1032
|
|
|
|
import (
|
|
"honnef.co/go/tools/analysis/callcheck"
|
|
"honnef.co/go/tools/analysis/lint"
|
|
"honnef.co/go/tools/go/ir"
|
|
"honnef.co/go/tools/internal/passes/buildir"
|
|
|
|
"golang.org/x/tools/go/analysis"
|
|
)
|
|
|
|
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
|
|
Analyzer: &analysis.Analyzer{
|
|
Name: "SA1032",
|
|
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
|
Run: callcheck.Analyzer(rules),
|
|
},
|
|
Doc: &lint.RawDocumentation{
|
|
Title: `Wrong order of arguments to \'errors.Is\'`,
|
|
Text: `
|
|
The first argument of the function \'errors.Is\' is the error
|
|
that we have and the second argument is the error we're trying to match against.
|
|
For example:
|
|
|
|
if errors.Is(err, io.EOF) { ... }
|
|
|
|
This check detects some cases where the two arguments have been swapped. It
|
|
flags any calls where the first argument is referring to a package-level error
|
|
variable, such as
|
|
|
|
if errors.Is(io.EOF, err) { /* this is wrong */ }`,
|
|
Since: "2024.1",
|
|
Severity: lint.SeverityError,
|
|
MergeIf: lint.MergeIfAny,
|
|
},
|
|
})
|
|
|
|
var Analyzer = SCAnalyzer.Analyzer
|
|
|
|
var rules = map[string]callcheck.Check{
|
|
"errors.Is": validateIs,
|
|
}
|
|
|
|
func validateIs(call *callcheck.Call) {
|
|
if len(call.Args) != 2 {
|
|
return
|
|
}
|
|
|
|
global := func(arg *callcheck.Argument) *ir.Global {
|
|
v, ok := arg.Value.Value.(*ir.Load)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
g, _ := v.X.(*ir.Global)
|
|
return g
|
|
}
|
|
|
|
x, y := call.Args[0], call.Args[1]
|
|
gx := global(x)
|
|
if gx == nil {
|
|
return
|
|
}
|
|
|
|
if pkgx := gx.Package().Pkg; pkgx != nil && pkgx.Path() != call.Pass.Pkg.Path() {
|
|
// x is a global that's not in this package
|
|
|
|
if gy := global(y); gy != nil {
|
|
if pkgy := gy.Package().Pkg; pkgy != nil && pkgy.Path() != call.Pass.Pkg.Path() {
|
|
// Both arguments refer to globals that aren't in this package. This can
|
|
// genuinely happen for external tests that check that one error "is"
|
|
// another one. net/http's external tests, for example, do
|
|
// `errors.Is(http.ErrNotSupported, errors.ErrUnsupported)`.
|
|
return
|
|
}
|
|
}
|
|
|
|
call.Invalid("arguments have the wrong order")
|
|
}
|
|
}
|