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

     1  // Copyright 2020 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  	"bytes"
     9  	"go/ast"
    10  	"go/format"
    11  	"go/token"
    12  
    13  	"golang.org/x/tools/go/ast/astutil"
    14  )
    15  
    16  // FuncParams checks function parameters and results.
    17  func FuncParams(fs *token.FileSet, f *ast.File, fix bool) (issues []*Issue) {
    18  	astutil.Apply(f, func(c *astutil.Cursor) (cont bool) {
    19  		// Always continue traversal.
    20  		cont = true
    21  
    22  		n, ok := c.Node().(*ast.FuncType)
    23  		if !ok {
    24  			return
    25  		}
    26  		for _, l := range []*ast.FieldList{n.Params, n.Results} {
    27  			if l == nil || l.List == nil || l.List[0].Names == nil {
    28  				continue
    29  			}
    30  			var nfis []*ast.Field
    31  			var names []*ast.Ident
    32  			var prev *ast.Field
    33  			var removable []ast.Expr
    34  			for _, fi := range l.List {
    35  				if prev == nil {
    36  					names = append(names, fi.Names...)
    37  					prev = fi
    38  					continue
    39  				}
    40  				// This seems to be always false (i.e. all the fields in
    41  				// non-nil prev are nil). We have it to be on the safe side.
    42  				shouldSeparate := prev.Doc != nil || prev.Tag != nil || prev.Comment != nil
    43  				if shouldSeparate || !sameType(fs, prev.Type, fi.Type) {
    44  					nfis = append(nfis, &ast.Field{
    45  						Doc:     prev.Doc,
    46  						Names:   names,
    47  						Type:    prev.Type,
    48  						Tag:     prev.Tag,
    49  						Comment: prev.Comment,
    50  					})
    51  					names = nil
    52  				} else {
    53  					removable = append(removable, prev.Type)
    54  				}
    55  				names = append(names, fi.Names...)
    56  				prev = fi
    57  			}
    58  			nfis = append(nfis, &ast.Field{
    59  				Doc:     prev.Doc,
    60  				Names:   names,
    61  				Type:    prev.Type,
    62  				Tag:     prev.Tag,
    63  				Comment: prev.Comment,
    64  			})
    65  			if fix {
    66  				l.List = nfis
    67  			} else {
    68  				for _, t := range removable {
    69  					issues = append(issues, &Issue{
    70  						Pos:     fs.Position(t.Pos()),
    71  						Msg:     "When two or more consecutive named function parameters share a type, you can omit the type from all but the last",
    72  						Link:    "https://tour.golang.org/basics/5",
    73  						Fixable: true,
    74  					})
    75  				}
    76  			}
    77  		}
    78  		return
    79  	}, nil)
    80  	return
    81  }
    82  
    83  // sameType returns whether x and y have the same string representation.
    84  // Both x and y should be representing a type.
    85  func sameType(fs *token.FileSet, x, y ast.Expr) bool {
    86  	var xb bytes.Buffer
    87  	if err := format.Node(&xb, fs, x); err != nil {
    88  		return false
    89  	}
    90  	var yb bytes.Buffer
    91  	if err := format.Node(&yb, fs, y); err != nil {
    92  		return false
    93  	}
    94  	return xb.String() == yb.String()
    95  }