src

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

s1040.go (2489B)


      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) (any, 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 }