...

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

Documentation: cmd/vendor/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  	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/shift",
    33  	Requires: []*analysis.Analyzer{inspect.Analyzer},
    34  	Run:      run,
    35  }
    36  
    37  func run(pass *analysis.Pass) (interface{}, error) {
    38  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    39  
    40  	// Do a complete pass to compute dead nodes.
    41  	dead := make(map[ast.Node]bool)
    42  	nodeFilter := []ast.Node{
    43  		(*ast.IfStmt)(nil),
    44  		(*ast.SwitchStmt)(nil),
    45  	}
    46  	inspect.Preorder(nodeFilter, func(n ast.Node) {
    47  		// TODO(adonovan): move updateDead into this file.
    48  		updateDead(pass.TypesInfo, dead, n)
    49  	})
    50  
    51  	nodeFilter = []ast.Node{
    52  		(*ast.AssignStmt)(nil),
    53  		(*ast.BinaryExpr)(nil),
    54  	}
    55  	inspect.Preorder(nodeFilter, func(node ast.Node) {
    56  		if dead[node] {
    57  			// Skip shift checks on unreachable nodes.
    58  			return
    59  		}
    60  
    61  		switch node := node.(type) {
    62  		case *ast.BinaryExpr:
    63  			if node.Op == token.SHL || node.Op == token.SHR {
    64  				checkLongShift(pass, node, node.X, node.Y)
    65  			}
    66  		case *ast.AssignStmt:
    67  			if len(node.Lhs) != 1 || len(node.Rhs) != 1 {
    68  				return
    69  			}
    70  			if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN {
    71  				checkLongShift(pass, node, node.Lhs[0], node.Rhs[0])
    72  			}
    73  		}
    74  	})
    75  	return nil, nil
    76  }
    77  
    78  // checkLongShift checks if shift or shift-assign operations shift by more than
    79  // the length of the underlying variable.
    80  func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
    81  	if pass.TypesInfo.Types[x].Value != nil {
    82  		// Ignore shifts of constants.
    83  		// These are frequently used for bit-twiddling tricks
    84  		// like ^uint(0) >> 63 for 32/64 bit detection and compatibility.
    85  		return
    86  	}
    87  
    88  	v := pass.TypesInfo.Types[y].Value
    89  	if v == nil {
    90  		return
    91  	}
    92  	amt, ok := constant.Int64Val(v)
    93  	if !ok {
    94  		return
    95  	}
    96  	t := pass.TypesInfo.Types[x].Type
    97  	if t == nil {
    98  		return
    99  	}
   100  	var structuralTypes []types.Type
   101  	switch t := t.(type) {
   102  	case *types.TypeParam:
   103  		terms, err := typeparams.StructuralTerms(t)
   104  		if err != nil {
   105  			return // invalid type
   106  		}
   107  		for _, term := range terms {
   108  			structuralTypes = append(structuralTypes, term.Type())
   109  		}
   110  	default:
   111  		structuralTypes = append(structuralTypes, t)
   112  	}
   113  	sizes := make(map[int64]struct{})
   114  	for _, t := range structuralTypes {
   115  		size := 8 * pass.TypesSizes.Sizeof(t)
   116  		sizes[size] = struct{}{}
   117  	}
   118  	minSize := int64(math.MaxInt64)
   119  	for size := range sizes {
   120  		if size < minSize {
   121  			minSize = size
   122  		}
   123  	}
   124  	if amt >= minSize {
   125  		ident := analysisutil.Format(pass.Fset, x)
   126  		qualifier := ""
   127  		if len(sizes) > 1 {
   128  			qualifier = "may be "
   129  		}
   130  		pass.ReportRangef(node, "%s (%s%d bits) too small for shift of %d", ident, qualifier, minSize, amt)
   131  	}
   132  }
   133  

View as plain text