s1034.go (3385B)
1 package s1034 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/edit" 10 "honnef.co/go/tools/analysis/facts/generated" 11 "honnef.co/go/tools/analysis/lint" 12 "honnef.co/go/tools/analysis/report" 13 "honnef.co/go/tools/pattern" 14 15 "golang.org/x/tools/go/analysis" 16 ) 17 18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 19 Analyzer: &analysis.Analyzer{ 20 Name: "S1034", 21 Run: run, 22 Requires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...), 23 }, 24 Doc: &lint.RawDocumentation{ 25 Title: `Use result of type assertion to simplify cases`, 26 Since: "2019.2", 27 MergeIf: lint.MergeIfAny, 28 }, 29 }) 30 31 var Analyzer = SCAnalyzer.Analyzer 32 33 var ( 34 checkSimplifyTypeSwitchQ = pattern.MustParse(` 35 (TypeSwitchStmt 36 nil 37 expr@(TypeAssertExpr ident@(Ident _) _) 38 body)`) 39 checkSimplifyTypeSwitchR = pattern.MustParse(`(AssignStmt ident ":=" expr)`) 40 ) 41 42 func run(pass *analysis.Pass) (any, error) { 43 for node, m := range code.Matches(pass, checkSimplifyTypeSwitchQ) { 44 stmt := node.(*ast.TypeSwitchStmt) 45 expr := m.State["expr"].(ast.Node) 46 ident := m.State["ident"].(*ast.Ident) 47 48 x := pass.TypesInfo.ObjectOf(ident) 49 var allOffenders []*ast.TypeAssertExpr 50 canSuggestFix := true 51 for _, clause := range stmt.Body.List { 52 clause := clause.(*ast.CaseClause) 53 if len(clause.List) != 1 { 54 continue 55 } 56 hasUnrelatedAssertion := false 57 var offenders []*ast.TypeAssertExpr 58 ast.Inspect(clause, func(node ast.Node) bool { 59 assert2, ok := node.(*ast.TypeAssertExpr) 60 if !ok { 61 return true 62 } 63 ident, ok := assert2.X.(*ast.Ident) 64 if !ok { 65 hasUnrelatedAssertion = true 66 return false 67 } 68 if pass.TypesInfo.ObjectOf(ident) != x { 69 hasUnrelatedAssertion = true 70 return false 71 } 72 73 if !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) { 74 hasUnrelatedAssertion = true 75 return false 76 } 77 offenders = append(offenders, assert2) 78 return true 79 }) 80 if !hasUnrelatedAssertion { 81 // don't flag cases that have other type assertions 82 // unrelated to the one in the case clause. often 83 // times, this is done for symmetry, when two 84 // different values have to be asserted to the same 85 // type. 86 allOffenders = append(allOffenders, offenders...) 87 } 88 canSuggestFix = canSuggestFix && !hasUnrelatedAssertion 89 } 90 if len(allOffenders) != 0 { 91 var opts []report.Option 92 for _, offender := range allOffenders { 93 opts = append(opts, report.Related(offender, "could eliminate this type assertion")) 94 } 95 opts = append(opts, report.FilterGenerated()) 96 97 msg := fmt.Sprintf("assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate type assertions in switch cases", 98 report.Render(pass, ident), report.Render(pass, ident)) 99 if canSuggestFix { 100 var edits []analysis.TextEdit 101 edits = append(edits, edit.ReplaceWithPattern(pass.Fset, expr, checkSimplifyTypeSwitchR, m.State)) 102 for _, offender := range allOffenders { 103 edits = append(edits, edit.ReplaceWithNode(pass.Fset, offender, offender.X)) 104 } 105 opts = append(opts, report.Fixes(edit.Fix("Simplify type switch", edits...))) 106 report.Report(pass, expr, msg, opts...) 107 } else { 108 report.Report(pass, expr, msg, opts...) 109 } 110 } 111 } 112 return nil, nil 113 }