package s1021 import ( "go/ast" "go/token" "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/edit" "honnef.co/go/tools/analysis/facts/generated" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" ) var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Analyzer: &analysis.Analyzer{ Name: "S1021", Run: run, Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, }, Doc: &lint.RawDocumentation{ Title: `Merge variable declaration and assignment`, Before: ` var x uint x = 1`, After: `var x uint = 1`, Since: "2017.1", MergeIf: lint.MergeIfAny, }, }) var Analyzer = SCAnalyzer.Analyzer func run(pass *analysis.Pass) (interface{}, error) { hasMultipleAssignments := func(root ast.Node, ident *ast.Ident) bool { num := 0 ast.Inspect(root, func(node ast.Node) bool { if num >= 2 { return false } assign, ok := node.(*ast.AssignStmt) if !ok { return true } for _, lhs := range assign.Lhs { if oident, ok := lhs.(*ast.Ident); ok { if pass.TypesInfo.ObjectOf(oident) == pass.TypesInfo.ObjectOf(ident) { num++ } } } return true }) return num >= 2 } fn := func(node ast.Node) { block := node.(*ast.BlockStmt) if len(block.List) < 2 { return } for i, stmt := range block.List[:len(block.List)-1] { _ = i decl, ok := stmt.(*ast.DeclStmt) if !ok { continue } gdecl, ok := decl.Decl.(*ast.GenDecl) if !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 { continue } vspec, ok := gdecl.Specs[0].(*ast.ValueSpec) if !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 { continue } assign, ok := block.List[i+1].(*ast.AssignStmt) if !ok || assign.Tok != token.ASSIGN { continue } if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { continue } ident, ok := assign.Lhs[0].(*ast.Ident) if !ok { continue } if pass.TypesInfo.ObjectOf(vspec.Names[0]) != pass.TypesInfo.ObjectOf(ident) { continue } if code.RefersTo(pass, assign.Rhs[0], pass.TypesInfo.ObjectOf(ident)) { continue } if hasMultipleAssignments(block, ident) { continue } r := &ast.GenDecl{ Specs: []ast.Spec{ &ast.ValueSpec{ Names: vspec.Names, Values: []ast.Expr{assign.Rhs[0]}, Type: vspec.Type, }, }, Tok: gdecl.Tok, } report.Report(pass, decl, "should merge variable declaration with assignment on next line", report.FilterGenerated(), report.Fixes(edit.Fix("merge declaration with assignment", edit.ReplaceWithNode(pass.Fset, edit.Range{decl.Pos(), assign.End()}, r)))) } } code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) return nil, nil }