1
2
3
4
5 package assign
6
7
8
9
10 import (
11 _ "embed"
12 "fmt"
13 "go/ast"
14 "go/token"
15 "go/types"
16 "reflect"
17
18 "golang.org/x/tools/go/analysis"
19 "golang.org/x/tools/go/analysis/passes/inspect"
20 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
21 "golang.org/x/tools/go/ast/inspector"
22 )
23
24
25 var doc string
26
27 var Analyzer = &analysis.Analyzer{
28 Name: "assign",
29 Doc: analysisutil.MustExtractDoc(doc, "assign"),
30 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/assign",
31 Requires: []*analysis.Analyzer{inspect.Analyzer},
32 Run: run,
33 }
34
35 func run(pass *analysis.Pass) (interface{}, error) {
36 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
37
38 nodeFilter := []ast.Node{
39 (*ast.AssignStmt)(nil),
40 }
41 inspect.Preorder(nodeFilter, func(n ast.Node) {
42 stmt := n.(*ast.AssignStmt)
43 if stmt.Tok != token.ASSIGN {
44 return
45 }
46 if len(stmt.Lhs) != len(stmt.Rhs) {
47
48 return
49 }
50 for i, lhs := range stmt.Lhs {
51 rhs := stmt.Rhs[i]
52 if analysisutil.HasSideEffects(pass.TypesInfo, lhs) ||
53 analysisutil.HasSideEffects(pass.TypesInfo, rhs) ||
54 isMapIndex(pass.TypesInfo, lhs) {
55 continue
56 }
57 if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
58 continue
59 }
60 le := analysisutil.Format(pass.Fset, lhs)
61 re := analysisutil.Format(pass.Fset, rhs)
62 if le == re {
63 pass.Report(analysis.Diagnostic{
64 Pos: stmt.Pos(), Message: fmt.Sprintf("self-assignment of %s to %s", re, le),
65 SuggestedFixes: []analysis.SuggestedFix{
66 {Message: "Remove", TextEdits: []analysis.TextEdit{
67 {Pos: stmt.Pos(), End: stmt.End(), NewText: []byte{}},
68 }},
69 },
70 })
71 }
72 }
73 })
74
75 return nil, nil
76 }
77
78
79 func isMapIndex(info *types.Info, e ast.Expr) bool {
80 if idx, ok := ast.Unparen(e).(*ast.IndexExpr); ok {
81 if typ := info.Types[idx.X].Type; typ != nil {
82 _, ok := typ.Underlying().(*types.Map)
83 return ok
84 }
85 }
86 return false
87 }
88
View as plain text