github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/testutils/lint/passes/nocopy/nocopy.go (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  // Package nocopy defines an Analyzer that detects invalid uses of util.NoCopy.
    12  package nocopy
    13  
    14  import (
    15  	"go/ast"
    16  
    17  	"golang.org/x/tools/go/analysis"
    18  	"golang.org/x/tools/go/analysis/passes/inspect"
    19  	"golang.org/x/tools/go/ast/inspector"
    20  )
    21  
    22  // Doc documents this pass.
    23  const Doc = `check for invalid uses of util.NoCopy`
    24  
    25  // Analyzer defines this pass.
    26  var Analyzer = &analysis.Analyzer{
    27  	Name:     "nocopy",
    28  	Doc:      Doc,
    29  	Requires: []*analysis.Analyzer{inspect.Analyzer},
    30  	Run:      run,
    31  }
    32  
    33  const noCopyType = "github.com/cockroachdb/cockroach/pkg/util.NoCopy"
    34  
    35  // nocopy ensures that the util.NoCopy type is not misused. Specifically, it
    36  // ensures that the type is always embedded without a name as the first field in
    37  // a parent struct like:
    38  //
    39  //     type s struct {
    40  //         _ util.NoCopy
    41  //         ...
    42  //     }
    43  //
    44  // We lint against including the type in other positions in structs both for
    45  // uniformity and because it can have runtime performance effects. Specifically,
    46  // if util.NoCopy is included as the last field in a parent struct then it will
    47  // increase the size of the parent struct even though util.NoCopy is zero-sized.
    48  // This is explained in detail in https://github.com/golang/go/issues/9401 and
    49  // is demonstrated in https://play.golang.org/p/jwB2Az5owcm.
    50  func run(pass *analysis.Pass) (interface{}, error) {
    51  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    52  	inspect.Preorder([]ast.Node{
    53  		(*ast.StructType)(nil),
    54  	}, func(n ast.Node) {
    55  		str := n.(*ast.StructType)
    56  		if str.Fields == nil {
    57  			return
    58  		}
    59  		for i, f := range str.Fields.List {
    60  			tv, ok := pass.TypesInfo.Types[f.Type]
    61  			if !ok {
    62  				continue
    63  			}
    64  			if tv.Type.String() != noCopyType {
    65  				continue
    66  			}
    67  			switch {
    68  			case i != 0:
    69  				pass.Reportf(f.Pos(), "Illegal use of util.NoCopy - must be first field in struct")
    70  			case len(f.Names) == 0:
    71  				pass.Reportf(f.Pos(), "Illegal use of util.NoCopy - should not be embedded")
    72  			case len(f.Names) > 1:
    73  				pass.Reportf(f.Pos(), "Illegal use of util.NoCopy - should be included only once")
    74  			case f.Names[0].Name != "_":
    75  				pass.Reportf(f.Pos(), "Illegal use of util.NoCopy - should be unnamed")
    76  			default:
    77  				// Valid use.
    78  			}
    79  		}
    80  	})
    81  	return nil, nil
    82  }