...

Source file src/golang.org/x/tools/go/analysis/passes/shift/shift.go

Documentation: golang.org/x/tools/go/analysis/passes/shift

     1  // Copyright 2014 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 shift defines an Analyzer that checks for shifts that exceed
     6  // the width of an integer.
     7  package shift
     8  
     9  // TODO(adonovan): integrate with ctrflow (CFG-based) dead code analysis. May
    10  // have impedance mismatch due to its (non-)treatment of constant
    11  // expressions (such as runtime.GOARCH=="386").
    12  
    13  import (
    14  	"go/ast"
    15  	"go/constant"
    16  	"go/token"
    17  	"go/types"
    18  	"math"
    19  
    20  	"golang.org/x/tools/go/analysis"
    21  	"golang.org/x/tools/go/analysis/passes/inspect"
    22  	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
    23  	"golang.org/x/tools/go/ast/inspector"
    24  	"golang.org/x/tools/internal/typeparams"
    25  )
    26  
    27  const Doc = "check for shifts that equal or exceed the width of the integer"
    28  
    29  var Analyzer = &analysis.Analyzer{
    30  	Name:     "shift",
    31  	Doc:      Doc,
    32  	Requires: []*analysis.Analyzer{inspect.Analyzer},
    33  	Run:      run,
    34  }
    35  
    36  func run(pass *analysis.Pass) (interface{}, error) {
    37  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    38  
    39  	// Do a complete pass to compute dead nodes.
    40  	dead := make(map[ast.Node]bool)
    41  	nodeFilter := []ast.Node{
    42  		(*ast.IfStmt)(nil),
    43  		(*ast.SwitchStmt)(nil),
    44  	}
    45  	inspect.Preorder(nodeFilter, func(n ast.Node) {
    46  		// TODO(adonovan): move updateDead into this file.
    47  		updateDead(pass.TypesInfo, dead, n)
    48  	})
    49  
    50  	nodeFilter = []ast.Node{
    51  		(*ast.AssignStmt)(nil),
    52  		(*ast.BinaryExpr)(nil),
    53  	}
    54  	inspect.Preorder(nodeFilter, func(node ast.Node) {
    55  		if dead[node] {
    56  			// Skip shift checks on unreachable nodes.
    57  			return
    58  		}
    59  
    60  		switch node := node.(type) {
    61  		case *ast.BinaryExpr:
    62  			if node.Op == token.SHL || node.Op == token.SHR {
    63  				checkLongShift(pass, node, node.X, node.Y)
    64  			}
    65  		case *ast.AssignStmt:
    66  			if len(node.Lhs) != 1 || len(node.Rhs) != 1 {
    67  				return
    68  			}
    69  			if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN {
    70  				checkLongShift(pass, node, node.Lhs[0], node.Rhs[0])
    71  			}
    72  		}
    73  	})
    74  	return nil, nil
    75  }
    76  
    77  // checkLongShift checks if shift or shift-assign operations shift by more than
    78  // the length of the underlying variable.
    79  func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
    80  	if pass.TypesInfo.Types[x].Value != nil {
    81  		// Ignore shifts of constants.
    82  		// These are frequently used for bit-twiddling tricks
    83  		// like ^uint(0) >> 63 for 32/64 bit detection and compatibility.
    84  		return
    85  	}
    86  
    87  	v := pass.TypesInfo.Types[y].Value
    88  	if v == nil {
    89  		return
    90  	}
    91  	amt, ok := constant.Int64Val(v)
    92  	if !ok {
    93  		return
    94  	}
    95  	t := pass.TypesInfo.Types[x].Type
    96  	if t == nil {
    97  		return
    98  	}
    99  	var structuralTypes []types.Type
   100  	switch t := t.(type) {
   101  	case *typeparams.TypeParam:
   102  		terms, err := typeparams.StructuralTerms(t)
   103  		if err != nil {
   104  			return // invalid type
   105  		}
   106  		for _, term := range terms {
   107  			structuralTypes = append(structuralTypes, term.Type())
   108  		}
   109  	default:
   110  		structuralTypes = append(structuralTypes, t)
   111  	}
   112  	sizes := make(map[int64]struct{})
   113  	for _, t := range structuralTypes {
   114  		size := 8 * pass.TypesSizes.Sizeof(t)
   115  		sizes[size] = struct{}{}
   116  	}
   117  	minSize := int64(math.MaxInt64)
   118  	for size := range sizes {
   119  		if size < minSize {
   120  			minSize = size
   121  		}
   122  	}
   123  	if amt >= minSize {
   124  		ident := analysisutil.Format(pass.Fset, x)
   125  		qualifier := ""
   126  		if len(sizes) > 1 {
   127  			qualifier = "may be "
   128  		}
   129  		pass.ReportRangef(node, "%s (%s%d bits) too small for shift of %d", ident, qualifier, minSize, amt)
   130  	}
   131  }
   132  

View as plain text