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