github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/context-keys-type.go (about)

     1  package rule
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/types"
     7  
     8  	"github.com/mgechev/revive/lint"
     9  )
    10  
    11  // ContextKeysType lints given else constructs.
    12  type ContextKeysType struct{}
    13  
    14  // Apply applies the rule to given file.
    15  func (r *ContextKeysType) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
    16  	var failures []lint.Failure
    17  
    18  	fileAst := file.AST
    19  	walker := lintContextKeyTypes{
    20  		file:    file,
    21  		fileAst: fileAst,
    22  		onFailure: func(failure lint.Failure) {
    23  			failures = append(failures, failure)
    24  		},
    25  	}
    26  
    27  	file.Pkg.TypeCheck()
    28  	ast.Walk(walker, fileAst)
    29  
    30  	return failures
    31  }
    32  
    33  // Name returns the rule name.
    34  func (r *ContextKeysType) Name() string {
    35  	return "context-keys-type"
    36  }
    37  
    38  type lintContextKeyTypes struct {
    39  	file      *lint.File
    40  	fileAst   *ast.File
    41  	onFailure func(lint.Failure)
    42  }
    43  
    44  func (w lintContextKeyTypes) Visit(n ast.Node) ast.Visitor {
    45  	switch n := n.(type) {
    46  	case *ast.CallExpr:
    47  		checkContextKeyType(w, n)
    48  	}
    49  
    50  	return w
    51  }
    52  
    53  func checkContextKeyType(w lintContextKeyTypes, x *ast.CallExpr) {
    54  	f := w.file
    55  	sel, ok := x.Fun.(*ast.SelectorExpr)
    56  	if !ok {
    57  		return
    58  	}
    59  	pkg, ok := sel.X.(*ast.Ident)
    60  	if !ok || pkg.Name != "context" {
    61  		return
    62  	}
    63  	if sel.Sel.Name != "WithValue" {
    64  		return
    65  	}
    66  
    67  	// key is second argument to context.WithValue
    68  	if len(x.Args) != 3 {
    69  		return
    70  	}
    71  	key := f.Pkg.TypesInfo.Types[x.Args[1]]
    72  
    73  	if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid {
    74  		w.onFailure(lint.Failure{
    75  			Confidence: 1,
    76  			Node:       x,
    77  			Category:   "content",
    78  			Failure:    fmt.Sprintf("should not use basic type %s as key in context.WithValue", key.Type),
    79  		})
    80  	}
    81  }