src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }