s1040.go raw

   1  package s1040
   2  
   3  import (
   4  	"fmt"
   5  	"go/ast"
   6  	"go/types"
   7  
   8  	"honnef.co/go/tools/analysis/code"
   9  	"honnef.co/go/tools/analysis/facts/generated"
  10  	"honnef.co/go/tools/analysis/lint"
  11  	"honnef.co/go/tools/analysis/report"
  12  
  13  	"golang.org/x/tools/go/analysis"
  14  	"golang.org/x/tools/go/analysis/passes/inspect"
  15  )
  16  
  17  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  18  	Analyzer: &analysis.Analyzer{
  19  		Name:     "S1040",
  20  		Run:      run,
  21  		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
  22  	},
  23  	Doc: &lint.RawDocumentation{
  24  		Title: "Type assertion to current type",
  25  		Text: `The type assertion \'x.(SomeInterface)\', when \'x\' already has type
  26  \'SomeInterface\', can only fail if \'x\' is nil. Usually, this is
  27  left-over code from when \'x\' had a different type and you can safely
  28  delete the type assertion. If you want to check that \'x\' is not nil,
  29  consider being explicit and using an actual \'if x == nil\' comparison
  30  instead of relying on the type assertion panicking.`,
  31  		Since: "2021.1",
  32  		// MergeIfAll because x might have different types under different build tags.
  33  		// You shouldn't write code like that…
  34  		MergeIf: lint.MergeIfAll,
  35  	},
  36  })
  37  
  38  var Analyzer = SCAnalyzer.Analyzer
  39  
  40  func run(pass *analysis.Pass) (interface{}, error) {
  41  	fn := func(node ast.Node) {
  42  		expr := node.(*ast.TypeAssertExpr)
  43  		if expr.Type == nil {
  44  			// skip type switches
  45  			//
  46  			// TODO(dh): we could flag type switches, too, when a case
  47  			// statement has the same type as expr.X – however,
  48  			// depending on the location of that case, it might behave
  49  			// identically to a default branch. we need to think
  50  			// carefully about the instances we want to flag. We also
  51  			// have to take nil interface values into consideration.
  52  			//
  53  			// It might make more sense to extend SA4020 to handle
  54  			// this.
  55  			return
  56  		}
  57  		t1 := pass.TypesInfo.TypeOf(expr.Type)
  58  		t2 := pass.TypesInfo.TypeOf(expr.X)
  59  		if types.IsInterface(t1) && types.Identical(t1, t2) {
  60  			report.Report(pass, expr,
  61  				fmt.Sprintf("type assertion to the same type: %s already has type %s", report.Render(pass, expr.X), report.Render(pass, expr.Type)),
  62  				report.FilterGenerated())
  63  		}
  64  	}
  65  
  66  	// TODO(dh): add suggested fixes. we need different fixes depending on the context:
  67  	// - assignment with 1 or 2 lhs
  68  	// - assignment to blank identifiers (as the first, second or both lhs)
  69  	// - initializers in if statements, with the same variations as above
  70  
  71  	code.Preorder(pass, fn, (*ast.TypeAssertExpr)(nil))
  72  	return nil, nil
  73  }
  74