github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1029/sa1029.go (about) 1 package sa1029 2 3 import ( 4 "fmt" 5 "go/types" 6 7 "github.com/amarpal/go-tools/analysis/callcheck" 8 "github.com/amarpal/go-tools/analysis/lint" 9 "github.com/amarpal/go-tools/internal/passes/buildir" 10 11 "golang.org/x/tools/go/analysis" 12 ) 13 14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 15 Analyzer: &analysis.Analyzer{ 16 Name: "SA1029", 17 Requires: []*analysis.Analyzer{buildir.Analyzer}, 18 Run: callcheck.Analyzer(checkWithValueKeyRules), 19 }, 20 Doc: &lint.Documentation{ 21 Title: `Inappropriate key in call to \'context.WithValue\'`, 22 Text: `The provided key must be comparable and should not be 23 of type \'string\' or any other built-in type to avoid collisions between 24 packages using context. Users of \'WithValue\' should define their own 25 types for keys. 26 27 To avoid allocating when assigning to an \'interface{}\', 28 context keys often have concrete type \'struct{}\'. Alternatively, 29 exported context key variables' static type should be a pointer or 30 interface.`, 31 Since: "2020.1", 32 Severity: lint.SeverityWarning, 33 MergeIf: lint.MergeIfAny, 34 }, 35 }) 36 37 var Analyzer = SCAnalyzer.Analyzer 38 39 var checkWithValueKeyRules = map[string]callcheck.Check{ 40 "context.WithValue": checkWithValueKey, 41 } 42 43 func checkWithValueKey(call *callcheck.Call) { 44 arg := call.Args[1] 45 T := arg.Value.Value.Type() 46 if T, ok := T.(*types.Basic); ok { 47 arg.Invalid( 48 fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", T)) 49 } 50 if !types.Comparable(T) { 51 arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T)) 52 } 53 }