honnef.co/go/tools@v0.5.0-0.dev.0.20240520180541-dcae280a5e87/staticcheck/sa1029/sa1029.go (about)

     1  package sa1029
     2  
     3  import (
     4  	"fmt"
     5  	"go/types"
     6  
     7  	"honnef.co/go/tools/analysis/callcheck"
     8  	"honnef.co/go/tools/analysis/lint"
     9  	"honnef.co/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 typ, ok := types.Unalias(T).(*types.Basic); ok {
    47  		if _, ok := T.(*types.Alias); ok {
    48  			arg.Invalid(
    49  				fmt.Sprintf("should not use built-in type %s (via alias %s) as key for value; define your own type to avoid collisions", typ, types.TypeString(T, types.RelativeTo(call.Pass.Pkg))))
    50  		} else {
    51  			arg.Invalid(
    52  				fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", typ))
    53  		}
    54  	}
    55  	if !types.Comparable(T) {
    56  		arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T))
    57  	}
    58  }