-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnilerr.go
83 lines (68 loc) · 1.64 KB
/
nilerr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// some code was copy from https://github.com/gostaticanalysis/nilerr/blob/master/nilerr.go
package nilnesserr
import (
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/ssa"
)
var errType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) // nolint: forcetypeassert
func isErrType(res ssa.Value) bool {
return types.Implements(res.Type(), errType)
}
func isConstNil(res ssa.Value) bool {
v, ok := res.(*ssa.Const)
if ok && v.IsNil() {
return true
}
return false
}
func extractCheckedErrorValue(binOp *ssa.BinOp) ssa.Value {
if isErrType(binOp.X) && isConstNil(binOp.Y) {
return binOp.X
}
if isErrType(binOp.Y) && isConstNil(binOp.X) {
return binOp.Y
}
return nil
}
type errFact fact
func findLastNonnilValue(errors []errFact, res ssa.Value) ssa.Value {
if len(errors) == 0 {
return nil
}
for j := len(errors) - 1; j >= 0; j-- {
last := errors[j]
if last.value == res {
return nil
} else if last.nilness == isnonnil {
return last.value
}
}
return nil
}
func checkNilnesserr(pass *analysis.Pass, b *ssa.BasicBlock, errors []errFact, isNilnees func(value ssa.Value) bool) {
for i := range b.Instrs {
instr, ok := b.Instrs[i].(*ssa.Return)
if !ok {
continue
}
for _, res := range instr.Results {
if !isErrType(res) || isConstNil(res) || !isNilnees(res) {
continue
}
// check the lastValue error that is isnonnil
lastValue := findLastNonnilValue(errors, res)
if lastValue == nil {
continue
}
// report
pos := instr.Pos()
if pos.IsValid() {
pass.Report(analysis.Diagnostic{
Pos: pos,
Message: linterMessage,
})
}
}
}
}