sa1016.go (3328B)
1 package sa1016 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "honnef.co/go/tools/analysis/code" 8 "honnef.co/go/tools/analysis/edit" 9 "honnef.co/go/tools/analysis/lint" 10 "honnef.co/go/tools/analysis/report" 11 "honnef.co/go/tools/pattern" 12 13 "golang.org/x/tools/go/analysis" 14 ) 15 16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 17 Analyzer: &analysis.Analyzer{ 18 Name: "SA1016", 19 Run: run, 20 Requires: code.RequiredAnalyzers, 21 }, 22 Doc: &lint.RawDocumentation{ 23 Title: `Trapping a signal that cannot be trapped`, 24 Text: `Not all signals can be intercepted by a process. Specifically, on 25 UNIX-like systems, the \'syscall.SIGKILL\' and \'syscall.SIGSTOP\' signals are 26 never passed to the process, but instead handled directly by the 27 kernel. It is therefore pointless to try and handle these signals.`, 28 Since: "2017.1", 29 Severity: lint.SeverityWarning, 30 MergeIf: lint.MergeIfAny, 31 }, 32 }) 33 34 var Analyzer = SCAnalyzer.Analyzer 35 36 var query = pattern.MustParse(` 37 (CallExpr 38 (Symbol 39 (Or 40 "os/signal.Ignore" 41 "os/signal.Notify" 42 "os/signal.Reset")) 43 _)`) 44 45 func run(pass *analysis.Pass) (any, error) { 46 isSignal := func(pass *analysis.Pass, expr ast.Expr, name string) bool { 47 if expr, ok := expr.(*ast.SelectorExpr); ok { 48 return code.SelectorName(pass, expr) == name 49 } else { 50 return false 51 } 52 } 53 54 for node := range code.Matches(pass, query) { 55 call := node.(*ast.CallExpr) 56 hasSigterm := false 57 for _, arg := range call.Args { 58 if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { 59 arg = conv.Args[0] 60 } 61 62 if isSignal(pass, arg, "syscall.SIGTERM") { 63 hasSigterm = true 64 break 65 } 66 67 } 68 for i, arg := range call.Args { 69 if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { 70 arg = conv.Args[0] 71 } 72 73 if isSignal(pass, arg, "os.Kill") || isSignal(pass, arg, "syscall.SIGKILL") { 74 var fixes []analysis.SuggestedFix 75 if !hasSigterm { 76 nargs := make([]ast.Expr, len(call.Args)) 77 for j, a := range call.Args { 78 if i == j { 79 nargs[j] = edit.Selector("syscall", "SIGTERM") 80 } else { 81 nargs[j] = a 82 } 83 } 84 ncall := *call 85 ncall.Args = nargs 86 fixes = append(fixes, edit.Fix(fmt.Sprintf("Use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) 87 } 88 nargs := make([]ast.Expr, 0, len(call.Args)) 89 for j, a := range call.Args { 90 if i == j { 91 continue 92 } 93 nargs = append(nargs, a) 94 } 95 ncall := *call 96 ncall.Args = nargs 97 fixes = append(fixes, edit.Fix(fmt.Sprintf("Remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) 98 report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...)) 99 } 100 if isSignal(pass, arg, "syscall.SIGSTOP") { 101 nargs := make([]ast.Expr, 0, len(call.Args)-1) 102 for j, a := range call.Args { 103 if i == j { 104 continue 105 } 106 nargs = append(nargs, a) 107 } 108 ncall := *call 109 ncall.Args = nargs 110 report.Report(pass, arg, "syscall.SIGSTOP cannot be trapped", report.Fixes(edit.Fix("Remove syscall.SIGSTOP from list of arguments", edit.ReplaceWithNode(pass.Fset, call, &ncall)))) 111 } 112 } 113 } 114 return nil, nil 115 }