sa1012.go (2119B)
1 package sa1012 2 3 import ( 4 "go/ast" 5 "go/types" 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/go/types/typeutil" 12 "honnef.co/go/tools/pattern" 13 14 "golang.org/x/tools/go/analysis" 15 ) 16 17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 18 Analyzer: &analysis.Analyzer{ 19 Name: "SA1012", 20 Run: run, 21 Requires: code.RequiredAnalyzers, 22 }, 23 Doc: &lint.RawDocumentation{ 24 Title: `A nil \'context.Context\' is being passed to a function, consider using \'context.TODO\' instead`, 25 Text: `The context package prohibits the use of a \'nil\' context. 26 If no parent context is available, a new context should be used, 27 e.g. \'context.TODO\' or \'context.Background\'.`, 28 Since: "2017.1", 29 Severity: lint.SeverityWarning, 30 MergeIf: lint.MergeIfAny, 31 }, 32 }) 33 34 var Analyzer = SCAnalyzer.Analyzer 35 36 var checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Symbol _) (Builtin "nil"):_)`) 37 38 func run(pass *analysis.Pass) (any, error) { 39 todo := &ast.CallExpr{ 40 Fun: edit.Selector("context", "TODO"), 41 } 42 bg := &ast.CallExpr{ 43 Fun: edit.Selector("context", "Background"), 44 } 45 for node, m := range code.Matches(pass, checkNilContextQ) { 46 call := node.(*ast.CallExpr) 47 fun, ok := m.State["fun"].(*types.Func) 48 if !ok { 49 // it might also be a builtin 50 continue 51 } 52 sig := fun.Type().(*types.Signature) 53 if sig.Params().Len() == 0 { 54 // Our CallExpr might've matched a method expression, like 55 // (*T).Foo(nil) – here, nil isn't the first argument of 56 // the Foo method, but the method receiver. 57 continue 58 } 59 if !typeutil.IsTypeWithName(sig.Params().At(0).Type(), "context.Context") { 60 continue 61 } 62 report.Report(pass, call.Args[0], 63 "do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use", report.Fixes( 64 edit.Fix("Use context.TODO", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)), 65 edit.Fix("Use context.Background", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg)))) 66 } 67 return nil, nil 68 }