github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/simple/s1040/s1040.go (about)

     1  package s1040
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/types"
     7  
     8  	"github.com/amarpal/go-tools/analysis/code"
     9  	"github.com/amarpal/go-tools/analysis/facts/generated"
    10  	"github.com/amarpal/go-tools/analysis/lint"
    11  	"github.com/amarpal/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.Documentation{
    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  }