Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go

     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/inspector"
    22  )
    23  
    24  //go:embed doc.go
    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 // ignore :=
    45  		}
    46  		if len(stmt.Lhs) != len(stmt.Rhs) {
    47  			// If LHS and RHS have different cardinality, they can't be the same.
    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 // expressions may not be equal
    56  			}
    57  			if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
    58  				continue // short-circuit the heavy-weight gofmt check
    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  // isMapIndex returns true if e is a map index expression.
    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