github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa2003/sa2003.go (about) 1 package sa2003 2 3 import ( 4 "fmt" 5 "go/types" 6 7 "github.com/amarpal/go-tools/analysis/lint" 8 "github.com/amarpal/go-tools/analysis/report" 9 "github.com/amarpal/go-tools/go/ir" 10 "github.com/amarpal/go-tools/go/ir/irutil" 11 "github.com/amarpal/go-tools/internal/passes/buildir" 12 13 "golang.org/x/tools/go/analysis" 14 ) 15 16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 17 Analyzer: &analysis.Analyzer{ 18 Name: "SA2003", 19 Run: run, 20 Requires: []*analysis.Analyzer{buildir.Analyzer}, 21 }, 22 Doc: &lint.Documentation{ 23 Title: `Deferred \'Lock\' right after locking, likely meant to defer \'Unlock\' instead`, 24 Since: "2017.1", 25 Severity: lint.SeverityWarning, 26 MergeIf: lint.MergeIfAny, 27 }, 28 }) 29 30 var Analyzer = SCAnalyzer.Analyzer 31 32 func run(pass *analysis.Pass) (interface{}, error) { 33 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 34 for _, block := range fn.Blocks { 35 instrs := irutil.FilterDebug(block.Instrs) 36 if len(instrs) < 2 { 37 continue 38 } 39 for i, ins := range instrs[:len(instrs)-1] { 40 call, ok := ins.(*ir.Call) 41 if !ok { 42 continue 43 } 44 if !irutil.IsCallToAny(call.Common(), "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { 45 continue 46 } 47 nins, ok := instrs[i+1].(*ir.Defer) 48 if !ok { 49 continue 50 } 51 if !irutil.IsCallToAny(&nins.Call, "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { 52 continue 53 } 54 if call.Common().Args[0] != nins.Call.Args[0] { 55 continue 56 } 57 name := shortCallName(call.Common()) 58 alt := "" 59 switch name { 60 case "Lock": 61 alt = "Unlock" 62 case "RLock": 63 alt = "RUnlock" 64 } 65 report.Report(pass, nins, fmt.Sprintf("deferring %s right after having locked already; did you mean to defer %s?", name, alt)) 66 } 67 } 68 } 69 return nil, nil 70 } 71 72 func shortCallName(call *ir.CallCommon) string { 73 if call.IsInvoke() { 74 return "" 75 } 76 switch v := call.Value.(type) { 77 case *ir.Function: 78 fn, ok := v.Object().(*types.Func) 79 if !ok { 80 return "" 81 } 82 return fn.Name() 83 case *ir.Builtin: 84 return v.Name() 85 } 86 return "" 87 }