github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa5010/sa5010.go (about) 1 package sa5010 2 3 import ( 4 "fmt" 5 "go/types" 6 7 "github.com/amarpal/go-tools/analysis/lint" 8 "github.com/amarpal/go-tools/analysis/report" 9 "github.com/amarpal/go-tools/go/ir" 10 "github.com/amarpal/go-tools/internal/passes/buildir" 11 12 "golang.org/x/tools/go/analysis" 13 ) 14 15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 16 Analyzer: &analysis.Analyzer{ 17 Name: "SA5010", 18 Run: run, 19 Requires: []*analysis.Analyzer{buildir.Analyzer}, 20 }, 21 Doc: &lint.Documentation{ 22 Title: `Impossible type assertion`, 23 24 Text: `Some type assertions can be statically proven to be 25 impossible. This is the case when the method sets of both 26 arguments of the type assertion conflict with each other, for 27 example by containing the same method with different 28 signatures. 29 30 The Go compiler already applies this check when asserting from an 31 interface value to a concrete type. If the concrete type misses 32 methods from the interface, or if function signatures don't match, 33 then the type assertion can never succeed. 34 35 This check applies the same logic when asserting from one interface to 36 another. If both interface types contain the same method but with 37 different signatures, then the type assertion can never succeed, 38 either.`, 39 40 Since: "2020.1", 41 Severity: lint.SeverityWarning, 42 // Technically this should be MergeIfAll, but the Go compiler 43 // already flags some impossible type assertions, so 44 // MergeIfAny is consistent with the compiler. 45 MergeIf: lint.MergeIfAny, 46 }, 47 }) 48 49 var Analyzer = SCAnalyzer.Analyzer 50 51 func run(pass *analysis.Pass) (interface{}, error) { 52 type entry struct { 53 l, r *types.Func 54 } 55 56 msc := &pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg.Prog.MethodSets 57 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 58 for _, b := range fn.Blocks { 59 for _, instr := range b.Instrs { 60 assert, ok := instr.(*ir.TypeAssert) 61 if !ok { 62 continue 63 } 64 var wrong []entry 65 left := assert.X.Type() 66 right := assert.AssertedType 67 righti, ok := right.Underlying().(*types.Interface) 68 69 if !ok { 70 // We only care about interface->interface 71 // assertions. The Go compiler already catches 72 // impossible interface->concrete assertions. 73 continue 74 } 75 76 ms := msc.MethodSet(left) 77 for i := 0; i < righti.NumMethods(); i++ { 78 mr := righti.Method(i).Origin() 79 sel := ms.Lookup(mr.Pkg(), mr.Name()) 80 if sel == nil { 81 continue 82 } 83 ml := sel.Obj().(*types.Func).Origin() 84 if types.AssignableTo(ml.Type(), mr.Type()) { 85 continue 86 } 87 88 wrong = append(wrong, entry{ml, mr}) 89 } 90 91 if len(wrong) != 0 { 92 s := fmt.Sprintf("impossible type assertion; %s and %s contradict each other:", 93 types.TypeString(left, types.RelativeTo(pass.Pkg)), 94 types.TypeString(right, types.RelativeTo(pass.Pkg))) 95 for _, e := range wrong { 96 s += fmt.Sprintf("\n\twrong type for %s method", e.l.Name()) 97 s += fmt.Sprintf("\n\t\thave %s", e.l.Type()) 98 s += fmt.Sprintf("\n\t\twant %s", e.r.Type()) 99 } 100 report.Report(pass, assert, s) 101 } 102 } 103 } 104 } 105 return nil, nil 106 }