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 }