github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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 defines an Analyzer that detects useless assignments. 6 package assign 7 8 // TODO(adonovan): check also for assignments to struct fields inside 9 // methods that are on T instead of *T. 10 11 import ( 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 const Doc = `check for useless assignments 25 26 This checker reports assignments of the form x = x or a[i] = a[i]. 27 These are almost always useless, and even when they aren't they are 28 usually a mistake.` 29 30 var Analyzer = &analysis.Analyzer{ 31 Name: "assign", 32 Doc: Doc, 33 Requires: []*analysis.Analyzer{inspect.Analyzer}, 34 Run: run, 35 } 36 37 func run(pass *analysis.Pass) (interface{}, error) { 38 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 39 40 nodeFilter := []ast.Node{ 41 (*ast.AssignStmt)(nil), 42 } 43 inspect.Preorder(nodeFilter, func(n ast.Node) { 44 stmt := n.(*ast.AssignStmt) 45 if stmt.Tok != token.ASSIGN { 46 return // ignore := 47 } 48 if len(stmt.Lhs) != len(stmt.Rhs) { 49 // If LHS and RHS have different cardinality, they can't be the same. 50 return 51 } 52 for i, lhs := range stmt.Lhs { 53 rhs := stmt.Rhs[i] 54 if analysisutil.HasSideEffects(pass.TypesInfo, lhs) || 55 analysisutil.HasSideEffects(pass.TypesInfo, rhs) || 56 isMapIndex(pass.TypesInfo, lhs) { 57 continue // expressions may not be equal 58 } 59 if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { 60 continue // short-circuit the heavy-weight gofmt check 61 } 62 le := analysisutil.Format(pass.Fset, lhs) 63 re := analysisutil.Format(pass.Fset, rhs) 64 if le == re { 65 pass.Report(analysis.Diagnostic{ 66 Pos: stmt.Pos(), Message: fmt.Sprintf("self-assignment of %s to %s", re, le), 67 SuggestedFixes: []analysis.SuggestedFix{ 68 {Message: "Remove", TextEdits: []analysis.TextEdit{ 69 {Pos: stmt.Pos(), End: stmt.End(), NewText: []byte{}}, 70 }}, 71 }, 72 }) 73 } 74 } 75 }) 76 77 return nil, nil 78 } 79 80 // isMapIndex returns true if e is a map index expression. 81 func isMapIndex(info *types.Info, e ast.Expr) bool { 82 if idx, ok := analysisutil.Unparen(e).(*ast.IndexExpr); ok { 83 if typ := info.Types[idx.X].Type; typ != nil { 84 _, ok := typ.Underlying().(*types.Map) 85 return ok 86 } 87 } 88 return false 89 }