golang.org/x/tools/gopls@v0.15.3/internal/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  	_ "embed"
    12  	"go/ast"
    13  	"go/format"
    14  	"go/token"
    15  
    16  	"golang.org/x/tools/go/analysis"
    17  	"golang.org/x/tools/go/analysis/passes/inspect"
    18  	"golang.org/x/tools/go/ast/inspector"
    19  	"golang.org/x/tools/internal/analysisinternal"
    20  )
    21  
    22  //go:embed doc.go
    23  var doc string
    24  
    25  var Analyzer = &analysis.Analyzer{
    26  	Name:             "nonewvars",
    27  	Doc:              analysisinternal.MustExtractDoc(doc, "nonewvars"),
    28  	Requires:         []*analysis.Analyzer{inspect.Analyzer},
    29  	Run:              run,
    30  	RunDespiteErrors: true,
    31  	URL:              "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/nonewvars",
    32  }
    33  
    34  func run(pass *analysis.Pass) (interface{}, error) {
    35  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    36  	if len(pass.TypeErrors) == 0 {
    37  		return nil, nil
    38  	}
    39  
    40  	nodeFilter := []ast.Node{(*ast.AssignStmt)(nil)}
    41  	inspect.Preorder(nodeFilter, func(n ast.Node) {
    42  		assignStmt, _ := n.(*ast.AssignStmt)
    43  		// We only care about ":=".
    44  		if assignStmt.Tok != token.DEFINE {
    45  			return
    46  		}
    47  
    48  		var file *ast.File
    49  		for _, f := range pass.Files {
    50  			if f.Pos() <= assignStmt.Pos() && assignStmt.Pos() < f.End() {
    51  				file = f
    52  				break
    53  			}
    54  		}
    55  		if file == nil {
    56  			return
    57  		}
    58  
    59  		for _, err := range pass.TypeErrors {
    60  			if !FixesError(err.Msg) {
    61  				continue
    62  			}
    63  			if assignStmt.Pos() > err.Pos || err.Pos >= assignStmt.End() {
    64  				continue
    65  			}
    66  			var buf bytes.Buffer
    67  			if err := format.Node(&buf, pass.Fset, file); err != nil {
    68  				continue
    69  			}
    70  			pass.Report(analysis.Diagnostic{
    71  				Pos:     err.Pos,
    72  				End:     analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos),
    73  				Message: err.Msg,
    74  				SuggestedFixes: []analysis.SuggestedFix{{
    75  					Message: "Change ':=' to '='",
    76  					TextEdits: []analysis.TextEdit{{
    77  						Pos: err.Pos,
    78  						End: err.Pos + 1,
    79  					}},
    80  				}},
    81  			})
    82  		}
    83  	})
    84  	return nil, nil
    85  }
    86  
    87  func FixesError(msg string) bool {
    88  	return msg == "no new variables on left side of :="
    89  }