github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/quickfix/qf1007/qf1007.go (about) 1 package qf1007 2 3 import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/amarpal/go-tools/analysis/code" 8 "github.com/amarpal/go-tools/analysis/edit" 9 "github.com/amarpal/go-tools/analysis/lint" 10 "github.com/amarpal/go-tools/analysis/report" 11 "github.com/amarpal/go-tools/pattern" 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: "QF1007", 20 Run: run, 21 Requires: []*analysis.Analyzer{inspect.Analyzer}, 22 }, 23 Doc: &lint.Documentation{ 24 Title: "Merge conditional assignment into variable declaration", 25 Before: ` 26 x := false 27 if someCondition { 28 x = true 29 }`, 30 After: `x := someCondition`, 31 Since: "2021.1", 32 Severity: lint.SeverityHint, 33 }, 34 }) 35 36 var Analyzer = SCAnalyzer.Analyzer 37 38 var checkConditionalAssignmentQ = pattern.MustParse(`(AssignStmt x@(Object _) ":=" assign@(Builtin b@(Or "true" "false")))`) 39 var checkConditionalAssignmentIfQ = pattern.MustParse(`(IfStmt nil cond [(AssignStmt x@(Object _) "=" (Builtin b@(Or "true" "false")))] nil)`) 40 41 func run(pass *analysis.Pass) (interface{}, error) { 42 fn := func(node ast.Node) { 43 var body *ast.BlockStmt 44 switch node := node.(type) { 45 case *ast.FuncDecl: 46 body = node.Body 47 case *ast.FuncLit: 48 body = node.Body 49 default: 50 panic("unreachable") 51 } 52 if body == nil { 53 return 54 } 55 56 stmts := body.List 57 if len(stmts) < 2 { 58 return 59 } 60 for i, first := range stmts[:len(stmts)-1] { 61 second := stmts[i+1] 62 m1, ok := code.Match(pass, checkConditionalAssignmentQ, first) 63 if !ok { 64 continue 65 } 66 m2, ok := code.Match(pass, checkConditionalAssignmentIfQ, second) 67 if !ok { 68 continue 69 } 70 if m1.State["x"] != m2.State["x"] { 71 continue 72 } 73 if m1.State["b"] == m2.State["b"] { 74 continue 75 } 76 77 v := m2.State["cond"].(ast.Expr) 78 if m1.State["b"] == "true" { 79 v = &ast.UnaryExpr{ 80 Op: token.NOT, 81 X: v, 82 } 83 } 84 report.Report(pass, first, "could merge conditional assignment into variable declaration", 85 report.Fixes(edit.Fix("Merge conditional assignment into variable declaration", 86 edit.ReplaceWithNode(pass.Fset, m1.State["assign"].(ast.Node), v), 87 edit.Delete(second)))) 88 } 89 } 90 code.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) 91 return nil, nil 92 }