github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/cmd/tast-lint/internal/check/define_emptyslice.go (about)

     1  // Copyright 2019 The ChromiumOS Authors
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package check
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/token"
    11  	"strings"
    12  
    13  	"golang.org/x/tools/go/ast/astutil"
    14  )
    15  
    16  // EmptySlice warns the invalid empty slice declaration.
    17  func EmptySlice(fs *token.FileSet, f *ast.File, fix bool) []*Issue {
    18  	var issues []*Issue
    19  
    20  	// Traverse a syntax tree and find not-preferred empty slice declarations.
    21  	astutil.Apply(f, func(c *astutil.Cursor) bool {
    22  		// If parent is not a block statement, ignore.
    23  		// - For example, empty slice assginment in the statement like
    24  		//     `if a := []int{}; len(a) == 1 { // parent is ast.IfStmt
    25  		//        return
    26  		//     }`
    27  		//   is not replacable by 'var' statement.
    28  		if _, ok := c.Parent().(*ast.BlockStmt); !ok {
    29  			return true
    30  		}
    31  
    32  		asgn, ok := c.Node().(*ast.AssignStmt)
    33  		if !ok {
    34  			return true
    35  		}
    36  		if asgn.Tok != token.DEFINE {
    37  			return true
    38  		}
    39  
    40  		// Find invalid empty slice declarations.
    41  		var ids []*ast.Ident
    42  		var elt ast.Expr
    43  
    44  		for i, rexp := range asgn.Rhs {
    45  			comp, ok := rexp.(*ast.CompositeLit)
    46  			if !ok {
    47  				continue
    48  			}
    49  
    50  			arr, ok := comp.Type.(*ast.ArrayType)
    51  			if !ok {
    52  				continue
    53  			}
    54  
    55  			if arr.Len == nil && comp.Elts == nil {
    56  				id, ok := asgn.Lhs[i].(*ast.Ident)
    57  				if !ok {
    58  					continue
    59  				}
    60  
    61  				ids = append(ids, id)
    62  				elt = arr.Elt
    63  			}
    64  		}
    65  
    66  		if len(ids) == 0 {
    67  			return true
    68  		}
    69  
    70  		fixable := len(asgn.Rhs) == 1
    71  		if !fix {
    72  			var idNames []string
    73  			for _, id := range ids {
    74  				idNames = append(idNames, id.Name)
    75  			}
    76  			issue := &Issue{
    77  				Pos:     fs.Position(asgn.Pos()),
    78  				Msg:     fmt.Sprintf("Use 'var' statement when you declare empty slice(s): %s", strings.Join(idNames, ", ")),
    79  				Link:    "https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices",
    80  				Fixable: fixable,
    81  			}
    82  			issues = append(issues, issue)
    83  		} else if fix && fixable {
    84  			c.Replace(&ast.DeclStmt{
    85  				Decl: &ast.GenDecl{
    86  					Tok:    token.VAR,
    87  					TokPos: ids[0].NamePos,
    88  					Specs: []ast.Spec{
    89  						&ast.ValueSpec{
    90  							Names: ids,
    91  							Type: &ast.ArrayType{
    92  								Elt: elt,
    93  							},
    94  						},
    95  					},
    96  				},
    97  			})
    98  		}
    99  
   100  		return true
   101  	}, nil)
   102  
   103  	return issues
   104  }