gitee.com/lonely0422/gometalinter.git@v3.0.1-0.20190307123442-32416ab75314+incompatible/_linters/src/github.com/mdempsky/maligned/maligned.go (about)

     1  // Copyright 2013 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 main
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/build"
    12  	"go/token"
    13  	"go/types"
    14  	"log"
    15  	"sort"
    16  
    17  	"github.com/kisielk/gotool"
    18  	"golang.org/x/tools/go/loader"
    19  )
    20  
    21  var fset = token.NewFileSet()
    22  
    23  func main() {
    24  	flag.Parse()
    25  
    26  	importPaths := gotool.ImportPaths(flag.Args())
    27  	if len(importPaths) == 0 {
    28  		return
    29  	}
    30  
    31  	var conf loader.Config
    32  	conf.Fset = fset
    33  	for _, importPath := range importPaths {
    34  		conf.Import(importPath)
    35  	}
    36  	prog, err := conf.Load()
    37  	if err != nil {
    38  		log.Fatal(err)
    39  	}
    40  
    41  	for _, pkg := range prog.InitialPackages() {
    42  		for _, file := range pkg.Files {
    43  			ast.Inspect(file, func(node ast.Node) bool {
    44  				if s, ok := node.(*ast.StructType); ok {
    45  					malign(node.Pos(), pkg.Types[s].Type.(*types.Struct))
    46  				}
    47  				return true
    48  			})
    49  		}
    50  	}
    51  }
    52  
    53  func malign(pos token.Pos, str *types.Struct) {
    54  	wordSize := int64(8)
    55  	maxAlign := int64(8)
    56  	switch build.Default.GOARCH {
    57  	case "386", "arm":
    58  		wordSize, maxAlign = 4, 4
    59  	case "amd64p32":
    60  		wordSize = 4
    61  	}
    62  
    63  	s := gcSizes{wordSize, maxAlign}
    64  	sz, opt := s.Sizeof(str), optimalSize(str, &s)
    65  	if sz != opt {
    66  		fmt.Printf("%s: struct of size %d could be %d\n", fset.Position(pos), sz, opt)
    67  	}
    68  }
    69  
    70  func optimalSize(str *types.Struct, sizes *gcSizes) int64 {
    71  	nf := str.NumFields()
    72  	fields := make([]*types.Var, nf)
    73  	alignofs := make([]int64, nf)
    74  	sizeofs := make([]int64, nf)
    75  	for i := 0; i < nf; i++ {
    76  		fields[i] = str.Field(i)
    77  		ft := fields[i].Type()
    78  		alignofs[i] = sizes.Alignof(ft)
    79  		sizeofs[i] = sizes.Sizeof(ft)
    80  	}
    81  	sort.Sort(&byAlignAndSize{fields, alignofs, sizeofs})
    82  	return sizes.Sizeof(types.NewStruct(fields, nil))
    83  }
    84  
    85  type byAlignAndSize struct {
    86  	fields   []*types.Var
    87  	alignofs []int64
    88  	sizeofs  []int64
    89  }
    90  
    91  func (s *byAlignAndSize) Len() int { return len(s.fields) }
    92  func (s *byAlignAndSize) Swap(i, j int) {
    93  	s.fields[i], s.fields[j] = s.fields[j], s.fields[i]
    94  	s.alignofs[i], s.alignofs[j] = s.alignofs[j], s.alignofs[i]
    95  	s.sizeofs[i], s.sizeofs[j] = s.sizeofs[j], s.sizeofs[i]
    96  }
    97  
    98  func (s *byAlignAndSize) Less(i, j int) bool {
    99  	// Place zero sized objects before non-zero sized objects.
   100  	if s.sizeofs[i] == 0 && s.sizeofs[j] != 0 {
   101  		return true
   102  	}
   103  	if s.sizeofs[j] == 0 && s.sizeofs[i] != 0 {
   104  		return false
   105  	}
   106  
   107  	// Next, place more tightly aligned objects before less tightly aligned objects.
   108  	if s.alignofs[i] != s.alignofs[j] {
   109  		return s.alignofs[i] > s.alignofs[j]
   110  	}
   111  
   112  	// Lastly, order by size.
   113  	if s.sizeofs[i] != s.sizeofs[j] {
   114  		return s.sizeofs[i] > s.sizeofs[j]
   115  	}
   116  
   117  	return false
   118  }
   119  
   120  // Code below based on go/types.StdSizes.
   121  
   122  type gcSizes struct {
   123  	WordSize int64
   124  	MaxAlign int64
   125  }
   126  
   127  func (s *gcSizes) Alignof(T types.Type) int64 {
   128  	// NOTE: On amd64, complex64 is 8 byte aligned,
   129  	// even though float32 is only 4 byte aligned.
   130  
   131  	// For arrays and structs, alignment is defined in terms
   132  	// of alignment of the elements and fields, respectively.
   133  	switch t := T.Underlying().(type) {
   134  	case *types.Array:
   135  		// spec: "For a variable x of array type: unsafe.Alignof(x)
   136  		// is the same as unsafe.Alignof(x[0]), but at least 1."
   137  		return s.Alignof(t.Elem())
   138  	case *types.Struct:
   139  		// spec: "For a variable x of struct type: unsafe.Alignof(x)
   140  		// is the largest of the values unsafe.Alignof(x.f) for each
   141  		// field f of x, but at least 1."
   142  		max := int64(1)
   143  		for i, nf := 0, t.NumFields(); i < nf; i++ {
   144  			if a := s.Alignof(t.Field(i).Type()); a > max {
   145  				max = a
   146  			}
   147  		}
   148  		return max
   149  	}
   150  	a := s.Sizeof(T) // may be 0
   151  	// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
   152  	if a < 1 {
   153  		return 1
   154  	}
   155  	if a > s.MaxAlign {
   156  		return s.MaxAlign
   157  	}
   158  	return a
   159  }
   160  
   161  var basicSizes = [...]byte{
   162  	types.Bool:       1,
   163  	types.Int8:       1,
   164  	types.Int16:      2,
   165  	types.Int32:      4,
   166  	types.Int64:      8,
   167  	types.Uint8:      1,
   168  	types.Uint16:     2,
   169  	types.Uint32:     4,
   170  	types.Uint64:     8,
   171  	types.Float32:    4,
   172  	types.Float64:    8,
   173  	types.Complex64:  8,
   174  	types.Complex128: 16,
   175  }
   176  
   177  func (s *gcSizes) Sizeof(T types.Type) int64 {
   178  	switch t := T.Underlying().(type) {
   179  	case *types.Basic:
   180  		k := t.Kind()
   181  		if int(k) < len(basicSizes) {
   182  			if s := basicSizes[k]; s > 0 {
   183  				return int64(s)
   184  			}
   185  		}
   186  		if k == types.String {
   187  			return s.WordSize * 2
   188  		}
   189  	case *types.Array:
   190  		n := t.Len()
   191  		if n == 0 {
   192  			return 0
   193  		}
   194  		a := s.Alignof(t.Elem())
   195  		z := s.Sizeof(t.Elem())
   196  		return align(z, a)*(n-1) + z
   197  	case *types.Slice:
   198  		return s.WordSize * 3
   199  	case *types.Struct:
   200  		nf := t.NumFields()
   201  		if nf == 0 {
   202  			return 0
   203  		}
   204  
   205  		var o int64
   206  		max := int64(1)
   207  		for i := 0; i < nf; i++ {
   208  			ft := t.Field(i).Type()
   209  			a, sz := s.Alignof(ft), s.Sizeof(ft)
   210  			if a > max {
   211  				max = a
   212  			}
   213  			if i == nf-1 && sz == 0 && o != 0 {
   214  				sz = 1
   215  			}
   216  			o = align(o, a) + sz
   217  		}
   218  		return align(o, max)
   219  	case *types.Interface:
   220  		return s.WordSize * 2
   221  	}
   222  	return s.WordSize // catch-all
   223  }
   224  
   225  // align returns the smallest y >= x such that y % a == 0.
   226  func align(x, a int64) int64 {
   227  	y := x + a - 1
   228  	return y - y%a
   229  }