golang.org/x/tools@v0.21.0/go/analysis/passes/assign/assign.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package assign 6 7 // TODO(adonovan): check also for assignments to struct fields inside 8 // methods that are on T instead of *T. 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/astutil" 22 "golang.org/x/tools/go/ast/inspector" 23 ) 24 25 //go:embed doc.go 26 var doc string 27 28 var Analyzer = &analysis.Analyzer{ 29 Name: "assign", 30 Doc: analysisutil.MustExtractDoc(doc, "assign"), 31 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/assign", 32 Requires: []*analysis.Analyzer{inspect.Analyzer}, 33 Run: run, 34 } 35 36 func run(pass *analysis.Pass) (interface{}, error) { 37 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 38 39 nodeFilter := []ast.Node{ 40 (*ast.AssignStmt)(nil), 41 } 42 inspect.Preorder(nodeFilter, func(n ast.Node) { 43 stmt := n.(*ast.AssignStmt) 44 if stmt.Tok != token.ASSIGN { 45 return // ignore := 46 } 47 if len(stmt.Lhs) != len(stmt.Rhs) { 48 // If LHS and RHS have different cardinality, they can't be the same. 49 return 50 } 51 for i, lhs := range stmt.Lhs { 52 rhs := stmt.Rhs[i] 53 if analysisutil.HasSideEffects(pass.TypesInfo, lhs) || 54 analysisutil.HasSideEffects(pass.TypesInfo, rhs) || 55 isMapIndex(pass.TypesInfo, lhs) { 56 continue // expressions may not be equal 57 } 58 if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { 59 continue // short-circuit the heavy-weight gofmt check 60 } 61 le := analysisutil.Format(pass.Fset, lhs) 62 re := analysisutil.Format(pass.Fset, rhs) 63 if le == re { 64 pass.Report(analysis.Diagnostic{ 65 Pos: stmt.Pos(), Message: fmt.Sprintf("self-assignment of %s to %s", re, le), 66 SuggestedFixes: []analysis.SuggestedFix{ 67 {Message: "Remove", TextEdits: []analysis.TextEdit{ 68 {Pos: stmt.Pos(), End: stmt.End(), NewText: []byte{}}, 69 }}, 70 }, 71 }) 72 } 73 } 74 }) 75 76 return nil, nil 77 } 78 79 // isMapIndex returns true if e is a map index expression. 80 func isMapIndex(info *types.Info, e ast.Expr) bool { 81 if idx, ok := astutil.Unparen(e).(*ast.IndexExpr); ok { 82 if typ := info.Types[idx.X].Type; typ != nil { 83 _, ok := typ.Underlying().(*types.Map) 84 return ok 85 } 86 } 87 return false 88 }