github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1016/sa1016.go (about) 1 package sa1016 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "github.com/amarpal/go-tools/analysis/code" 8 "github.com/amarpal/go-tools/analysis/edit" 9 "github.com/amarpal/go-tools/analysis/lint" 10 "github.com/amarpal/go-tools/analysis/report" 11 12 "golang.org/x/tools/go/analysis" 13 "golang.org/x/tools/go/analysis/passes/inspect" 14 ) 15 16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 17 Analyzer: &analysis.Analyzer{ 18 Name: "SA1016", 19 Run: run, 20 Requires: []*analysis.Analyzer{inspect.Analyzer}, 21 }, 22 Doc: &lint.Documentation{ 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 func run(pass *analysis.Pass) (interface{}, error) { 37 isSignal := func(pass *analysis.Pass, expr ast.Expr, name string) bool { 38 if expr, ok := expr.(*ast.SelectorExpr); ok { 39 return code.SelectorName(pass, expr) == name 40 } else { 41 return false 42 } 43 } 44 45 fn := func(node ast.Node) { 46 call := node.(*ast.CallExpr) 47 if !code.IsCallToAny(pass, call, 48 "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") { 49 return 50 } 51 52 hasSigterm := false 53 for _, arg := range call.Args { 54 if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { 55 arg = conv.Args[0] 56 } 57 58 if isSignal(pass, arg, "syscall.SIGTERM") { 59 hasSigterm = true 60 break 61 } 62 63 } 64 for i, arg := range call.Args { 65 if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { 66 arg = conv.Args[0] 67 } 68 69 if isSignal(pass, arg, "os.Kill") || isSignal(pass, arg, "syscall.SIGKILL") { 70 var fixes []analysis.SuggestedFix 71 if !hasSigterm { 72 nargs := make([]ast.Expr, len(call.Args)) 73 for j, a := range call.Args { 74 if i == j { 75 nargs[j] = edit.Selector("syscall", "SIGTERM") 76 } else { 77 nargs[j] = a 78 } 79 } 80 ncall := *call 81 ncall.Args = nargs 82 fixes = append(fixes, edit.Fix(fmt.Sprintf("use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) 83 } 84 nargs := make([]ast.Expr, 0, len(call.Args)) 85 for j, a := range call.Args { 86 if i == j { 87 continue 88 } 89 nargs = append(nargs, a) 90 } 91 ncall := *call 92 ncall.Args = nargs 93 fixes = append(fixes, edit.Fix(fmt.Sprintf("remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) 94 report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...)) 95 } 96 if isSignal(pass, arg, "syscall.SIGSTOP") { 97 nargs := make([]ast.Expr, 0, len(call.Args)-1) 98 for j, a := range call.Args { 99 if i == j { 100 continue 101 } 102 nargs = append(nargs, a) 103 } 104 ncall := *call 105 ncall.Args = nargs 106 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)))) 107 } 108 } 109 } 110 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 111 return nil, nil 112 }