src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

qf1007.go (2282B)


      1 package qf1007
      2 
      3 import (
      4 	"go/ast"
      5 	"go/token"
      6 
      7 	"honnef.co/go/tools/analysis/code"
      8 	"honnef.co/go/tools/analysis/edit"
      9 	"honnef.co/go/tools/analysis/lint"
     10 	"honnef.co/go/tools/analysis/report"
     11 	"honnef.co/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.RawDocumentation{
     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) (any, 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 }