src

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

s1021.go (2818B)


      1 package s1021
      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/facts/generated"
     10 	"honnef.co/go/tools/analysis/lint"
     11 	"honnef.co/go/tools/analysis/report"
     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:     "S1021",
     20 		Run:      run,
     21 		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
     22 	},
     23 	Doc: &lint.RawDocumentation{
     24 		Title: `Merge variable declaration and assignment`,
     25 		Before: `
     26 var x uint
     27 x = 1`,
     28 		After:   `var x uint = 1`,
     29 		Since:   "2017.1",
     30 		MergeIf: lint.MergeIfAny,
     31 	},
     32 })
     33 
     34 var Analyzer = SCAnalyzer.Analyzer
     35 
     36 func run(pass *analysis.Pass) (any, error) {
     37 	hasMultipleAssignments := func(root ast.Node, ident *ast.Ident) bool {
     38 		num := 0
     39 		ast.Inspect(root, func(node ast.Node) bool {
     40 			if num >= 2 {
     41 				return false
     42 			}
     43 			assign, ok := node.(*ast.AssignStmt)
     44 			if !ok {
     45 				return true
     46 			}
     47 			for _, lhs := range assign.Lhs {
     48 				if oident, ok := lhs.(*ast.Ident); ok {
     49 					if pass.TypesInfo.ObjectOf(oident) == pass.TypesInfo.ObjectOf(ident) {
     50 						num++
     51 					}
     52 				}
     53 			}
     54 
     55 			return true
     56 		})
     57 		return num >= 2
     58 	}
     59 	fn := func(node ast.Node) {
     60 		block := node.(*ast.BlockStmt)
     61 		if len(block.List) < 2 {
     62 			return
     63 		}
     64 		for i, stmt := range block.List[:len(block.List)-1] {
     65 			_ = i
     66 			decl, ok := stmt.(*ast.DeclStmt)
     67 			if !ok {
     68 				continue
     69 			}
     70 			gdecl, ok := decl.Decl.(*ast.GenDecl)
     71 			if !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 {
     72 				continue
     73 			}
     74 			vspec, ok := gdecl.Specs[0].(*ast.ValueSpec)
     75 			if !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 {
     76 				continue
     77 			}
     78 
     79 			assign, ok := block.List[i+1].(*ast.AssignStmt)
     80 			if !ok || assign.Tok != token.ASSIGN {
     81 				continue
     82 			}
     83 			if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
     84 				continue
     85 			}
     86 			ident, ok := assign.Lhs[0].(*ast.Ident)
     87 			if !ok {
     88 				continue
     89 			}
     90 			if pass.TypesInfo.ObjectOf(vspec.Names[0]) != pass.TypesInfo.ObjectOf(ident) {
     91 				continue
     92 			}
     93 
     94 			if code.RefersTo(pass, assign.Rhs[0], pass.TypesInfo.ObjectOf(ident)) {
     95 				continue
     96 			}
     97 			if hasMultipleAssignments(block, ident) {
     98 				continue
     99 			}
    100 
    101 			r := &ast.GenDecl{
    102 				Specs: []ast.Spec{
    103 					&ast.ValueSpec{
    104 						Names:  vspec.Names,
    105 						Values: []ast.Expr{assign.Rhs[0]},
    106 						Type:   vspec.Type,
    107 					},
    108 				},
    109 				Tok: gdecl.Tok,
    110 			}
    111 			report.Report(pass, decl, "should merge variable declaration with assignment on next line",
    112 				report.FilterGenerated(),
    113 				report.Fixes(edit.Fix("Merge declaration with assignment", edit.ReplaceWithNode(pass.Fset, edit.Range{decl.Pos(), assign.End()}, r))))
    114 		}
    115 	}
    116 	code.Preorder(pass, fn, (*ast.BlockStmt)(nil))
    117 	return nil, nil
    118 }