src

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

sa1029.go (2136B)


      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.RawDocumentation{
     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 	// TODO(dh): we should probably flag all anonymous structs, as they all risk collisions
     56 	if s, ok := T.(*types.Struct); ok && s.NumFields() == 0 {
     57 		arg.Invalid("should not use empty anonymous struct as key for value; define your own type to avoid collisions")
     58 	} else if !types.Comparable(T) {
     59 		arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T))
     60 	}
     61 }