github.com/v2fly/tools@v0.100.0/internal/lsp/analysis/nonewvars/nonewvars.go (about)

     1  // Copyright 2020 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 nonewvars defines an Analyzer that applies suggested fixes
     6  // to errors of the type "no new variables on left side of :=".
     7  package nonewvars
     8  
     9  import (
    10  	"bytes"
    11  	"go/ast"
    12  	"go/format"
    13  	"go/token"
    14  
    15  	"github.com/v2fly/tools/go/analysis"
    16  	"github.com/v2fly/tools/go/analysis/passes/inspect"
    17  	"github.com/v2fly/tools/go/ast/inspector"
    18  	"github.com/v2fly/tools/internal/analysisinternal"
    19  )
    20  
    21  const Doc = `suggested fixes for "no new vars on left side of :="
    22  
    23  This checker provides suggested fixes for type errors of the
    24  type "no new vars on left side of :=". For example:
    25  	z := 1
    26  	z := 2
    27  will turn into
    28  	z := 1
    29  	z = 2
    30  `
    31  
    32  var Analyzer = &analysis.Analyzer{
    33  	Name:             string(analysisinternal.NoNewVars),
    34  	Doc:              Doc,
    35  	Requires:         []*analysis.Analyzer{inspect.Analyzer},
    36  	Run:              run,
    37  	RunDespiteErrors: true,
    38  }
    39  
    40  func run(pass *analysis.Pass) (interface{}, error) {
    41  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    42  	errors := analysisinternal.GetTypeErrors(pass)
    43  
    44  	nodeFilter := []ast.Node{(*ast.AssignStmt)(nil)}
    45  	inspect.Preorder(nodeFilter, func(n ast.Node) {
    46  		assignStmt, _ := n.(*ast.AssignStmt)
    47  		// We only care about ":=".
    48  		if assignStmt.Tok != token.DEFINE {
    49  			return
    50  		}
    51  
    52  		var file *ast.File
    53  		for _, f := range pass.Files {
    54  			if f.Pos() <= assignStmt.Pos() && assignStmt.Pos() < f.End() {
    55  				file = f
    56  				break
    57  			}
    58  		}
    59  		if file == nil {
    60  			return
    61  		}
    62  
    63  		for _, err := range errors {
    64  			if !FixesError(err.Msg) {
    65  				continue
    66  			}
    67  			if assignStmt.Pos() > err.Pos || err.Pos >= assignStmt.End() {
    68  				continue
    69  			}
    70  			var buf bytes.Buffer
    71  			if err := format.Node(&buf, pass.Fset, file); err != nil {
    72  				continue
    73  			}
    74  			pass.Report(analysis.Diagnostic{
    75  				Pos:     err.Pos,
    76  				End:     analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos),
    77  				Message: err.Msg,
    78  				SuggestedFixes: []analysis.SuggestedFix{{
    79  					Message: "Change ':=' to '='",
    80  					TextEdits: []analysis.TextEdit{{
    81  						Pos: err.Pos,
    82  						End: err.Pos + 1,
    83  					}},
    84  				}},
    85  			})
    86  		}
    87  	})
    88  	return nil, nil
    89  }
    90  
    91  func FixesError(msg string) bool {
    92  	return msg == "no new variables on left side of :="
    93  }